diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs index e2248b4e2..ad0077736 100644 --- a/.maintain/frame-weight-template.hbs +++ b/.maintain/frame-weight-template.hbs @@ -47,22 +47,22 @@ impl WeightInfo for SubstrateWeight { {{~#each benchmark.components as |c| ~}} {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} ) -> Weight { - Weight::from_ref_time({{underscore benchmark.base_weight}} as u64) + Weight::from_ref_time({{underscore benchmark.base_weight}}_u64) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}} as u64).saturating_mul({{cw.name}} as u64)) + .saturating_add(Weight::from_ref_time({{underscore cw.slope}}_u64).saturating_mul({{cw.name}} as u64)) {{/each}} {{#if (ne benchmark.base_reads "0")}} - .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as u64)) + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) {{/if}} {{#each benchmark.component_reads as |cr|}} - .saturating_add(T::DbWeight::get().reads(({{cr.slope}} as u64).saturating_mul({{cr.name}} as u64))) + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}} as u64))) {{/each}} {{#if (ne benchmark.base_writes "0")}} - .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as u64)) + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) {{/if}} {{#each benchmark.component_writes as |cw|}} - .saturating_add(T::DbWeight::get().writes(({{cw.slope}} as u64).saturating_mul({{cw.name}} as u64))) + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}} as u64))) {{/each}} } {{/each}} @@ -82,22 +82,22 @@ impl WeightInfo for () { {{~#each benchmark.components as |c| ~}} {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} ) -> Weight { - Weight::from_ref_time({{underscore benchmark.base_weight}} as u64) + Weight::from_ref_time({{underscore benchmark.base_weight}}_u64) {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}} as u64).saturating_mul({{cw.name}} as u64)) + .saturating_add(Weight::from_ref_time({{underscore cw.slope}}_u64).saturating_mul({{cw.name}} as u64)) {{/each}} {{#if (ne benchmark.base_reads "0")}} - .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}} as u64)) + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) {{/if}} {{#each benchmark.component_reads as |cr|}} - .saturating_add(RocksDbWeight::get().reads(({{cr.slope}} as u64).saturating_mul({{cr.name}} as u64))) + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}} as u64))) {{/each}} {{#if (ne benchmark.base_writes "0")}} - .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}} as u64)) + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) {{/if}} {{#each benchmark.component_writes as |cw|}} - .saturating_add(RocksDbWeight::get().writes(({{cw.slope}} as u64).saturating_mul({{cw.name}} as u64))) + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}} as u64))) {{/each}} } {{/each}} diff --git a/Cargo.lock b/Cargo.lock index 66d8d13da..e75b40eaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.0", + "gimli 0.28.1", ] [[package]] @@ -211,12 +211,12 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37875bd9915b7d67c2f117ea2c30a0989874d0b2cb694fe25403c85763c0c9e" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", - "event-listener 3.1.0", + "event-listener 4.0.0", "event-listener-strategy", "futures-core", "pin-project-lite 0.2.13", @@ -224,30 +224,30 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 2.8.0", + "async-lock 3.2.0", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 1.13.0", + "futures-lite 2.1.0", "slab", ] [[package]] name = "async-global-executor" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +checksum = "9b4353121d5644cdf2beb5726ab752e79a8db1ebb52031770ec47db31d245526" dependencies = [ - "async-channel 1.9.0", + "async-channel 2.1.1", "async-executor", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io 2.2.1", + "async-lock 3.2.0", "blocking", - "futures-lite 1.13.0", + "futures-lite 2.1.0", "once_cell", ] @@ -273,22 +273,21 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997" +checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" dependencies = [ - "async-lock 3.1.0", + "async-lock 3.2.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "parking", - "polling 3.3.0", - "rustix 0.38.21", + "polling 3.3.1", + "rustix 0.38.27", "slab", "tracing", - "waker-fn", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -302,11 +301,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb2ab2aa8a746e221ab826c73f48bc6ba41be6763f0855cb249eb6d154cf1d7" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ - "event-listener 3.1.0", + "event-listener 4.0.0", "event-listener-strategy", "pin-project-lite 0.2.13", ] @@ -324,7 +323,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.21", + "rustix 0.38.27", "windows-sys 0.48.0", ] @@ -334,13 +333,13 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.2.0", + "async-io 2.2.1", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.21", + "rustix 0.38.27", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -660,12 +659,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.0", - "async-lock 3.1.0", + "async-channel 2.1.1", + "async-lock 3.2.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "piper", "tracing", ] @@ -756,9 +755,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" dependencies = [ "serde", ] @@ -778,10 +777,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.84" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -1184,7 +1184,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1283,9 +1283,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -1310,9 +1310,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -1320,9 +1320,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core2" @@ -1625,15 +1625,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-encoding-macro" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1641,9 +1641,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" dependencies = [ "data-encoding", "syn 1.0.109", @@ -1665,6 +1665,7 @@ name = "ddc-traits" version = "0.1.0" dependencies = [ "ddc-primitives", + "frame-support", "frame-system", "parity-scale-codec", "scale-info", @@ -1972,12 +1973,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2007,13 +2008,24 @@ dependencies = [ "pin-project-lite 0.2.13", ] +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + [[package]] name = "event-listener-strategy" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 3.1.0", + "event-listener 4.0.0", "pin-project-lite 0.2.13", ] @@ -2074,9 +2086,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f69037fe1b785e84986b4f2cbcf647381876a00671d25ceef715d7812dd7e1dd" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" [[package]] name = "file-per-thread-logger" @@ -2090,14 +2102,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", ] [[package]] @@ -2161,9 +2173,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -2247,7 +2259,7 @@ name = "frame-election-provider-solution-type" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -2350,7 +2362,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -2520,11 +2532,14 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ + "fastrand 2.0.1", "futures-core", + "futures-io", + "parking", "pin-project-lite 0.2.13", ] @@ -2671,9 +2686,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -2683,15 +2698,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", "bstr", - "fnv", "log", - "regex", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -2719,9 +2734,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -2729,7 +2744,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -2776,9 +2791,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -2985,9 +3000,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3068,7 +3083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -3146,7 +3161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", - "rustix 0.38.21", + "rustix 0.38.27", "windows-sys 0.48.0", ] @@ -3171,11 +3186,20 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -3272,7 +3296,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -4076,9 +4100,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -4189,7 +4213,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.27", ] [[package]] @@ -4265,9 +4289,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -4326,7 +4350,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro-error", "proc-macro2", "quote", @@ -4622,9 +4646,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -4931,6 +4955,7 @@ version = "4.8.1" dependencies = [ "ddc-primitives", "ddc-traits", + "frame-benchmarking", "frame-support", "frame-system", "hex", @@ -4942,6 +4967,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", + "serde", "sp-core", "sp-io", "sp-runtime", @@ -4956,12 +4982,14 @@ version = "0.1.0" dependencies = [ "ddc-primitives", "ddc-traits", + "frame-benchmarking", "frame-support", "frame-system", "log", "pallet-balances", "pallet-timestamp", "parity-scale-codec", + "rand_chacha 0.2.2", "scale-info", "sp-core", "sp-io", @@ -5001,6 +5029,7 @@ version = "4.8.1" dependencies = [ "ddc-primitives", "ddc-traits", + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", @@ -5048,9 +5077,11 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "lazy_static", "pallet-balances", "pallet-timestamp", "parity-scale-codec", + "parking_lot 0.12.1", "scale-info", "sp-core", "sp-io", @@ -5520,7 +5551,7 @@ name = "pallet-staking-reward-curve" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -5688,9 +5719,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -5703,11 +5734,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 1.0.109", @@ -5812,7 +5843,7 @@ dependencies = [ "libc", "redox_syscall 0.4.1", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -5847,9 +5878,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -6002,16 +6033,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite 0.2.13", - "rustix 0.38.21", + "rustix 0.38.27", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -6098,6 +6129,16 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -6124,9 +6165,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -6466,15 +6507,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -6626,9 +6658,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom 0.2.11", @@ -6741,7 +6773,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", - "errno 0.3.6", + "errno 0.3.8", "io-lifetimes 1.0.11", "libc", "linux-raw-sys 0.3.8", @@ -6750,15 +6782,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac" dependencies = [ "bitflags 2.4.1", - "errno 0.3.6", + "errno 0.3.8", "libc", - "linux-raw-sys 0.4.11", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", ] [[package]] @@ -6943,7 +6975,7 @@ name = "sc-chain-spec-derive" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -7823,7 +7855,7 @@ name = "sc-tracing-proc-macro" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -7901,7 +7933,7 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -7946,7 +7978,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -8048,18 +8080,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -8224,9 +8256,9 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "snap" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "snow" @@ -8239,7 +8271,7 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek 4.1.1", "rand_core 0.6.4", - "ring 0.17.5", + "ring 0.17.7", "rustc_version 0.4.0", "sha2 0.10.8", "subtle", @@ -8305,7 +8337,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ "blake2", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -8750,7 +8782,7 @@ version = "5.0.0" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ "Inflector", - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -9190,7 +9222,7 @@ name = "substrate-test-utils-derive" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.30#a3ed0119c45cdd0d571ad34e5b3ee7518c8cef8d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -9295,15 +9327,15 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.4.1", - "rustix 0.38.21", + "rustix 0.38.27", "windows-sys 0.48.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] @@ -9480,6 +9512,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -9639,9 +9688,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try-runtime-cli" @@ -9722,9 +9771,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -9789,20 +9838,20 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", ] [[package]] name = "utf8-width" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "valuable" @@ -9873,9 +9922,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -9883,9 +9932,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -9898,9 +9947,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -9910,9 +9959,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9920,9 +9969,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -9933,9 +9982,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-gc-api" @@ -10186,9 +10235,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -10200,7 +10249,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -10222,7 +10271,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.21", + "rustix 0.38.27", ] [[package]] @@ -10281,7 +10330,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -10318,7 +10367,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -10336,6 +10394,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -10348,6 +10421,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.34.0" @@ -10372,6 +10451,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.34.0" @@ -10396,6 +10481,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.34.0" @@ -10420,6 +10511,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.34.0" @@ -10444,6 +10541,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -10456,6 +10559,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" @@ -10480,6 +10589,21 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -10526,9 +10650,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs index 1559b913a..726fa96d8 100644 --- a/node/service/src/chain_spec.rs +++ b/node/service/src/chain_spec.rs @@ -235,6 +235,7 @@ pub fn cere_dev_genesis( transaction_payment: Default::default(), ddc_customers: Default::default(), nomination_pools: Default::default(), + ddc_clusters: Default::default(), } } diff --git a/pallets/ddc-clusters/Cargo.toml b/pallets/ddc-clusters/Cargo.toml index ac1c569aa..98abf9897 100644 --- a/pallets/ddc-clusters/Cargo.toml +++ b/pallets/ddc-clusters/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } ddc-primitives = { version = "0.1.0", default-features = false, path = "../../primitives" } ddc-traits = { version = "0.1.0", default-features = false, path = "../../traits" } +frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", optional = true } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } hex = { version = "0.4", default-features = false } @@ -14,6 +15,7 @@ hex-literal = "^0.3.1" pallet-contracts = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } pallet-ddc-nodes = { version = "4.7.0", default-features = false, path = "../ddc-nodes" } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } +serde = { version = "1.0.136", default-features = false, features = ["derive"], optional = true } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } @@ -23,6 +25,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } pallet-timestamp = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-tracing = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } substrate-test-utils = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } @@ -30,9 +33,11 @@ substrate-test-utils = { git = "https://github.com/paritytech/substrate.git", br default = ["std"] std = [ "codec/std", + "serde", "ddc-primitives/std", "frame-support/std", "frame-system/std", + "frame-benchmarking/std", "pallet-contracts/std", "pallet-ddc-nodes/std", "scale-info/std", @@ -40,3 +45,10 @@ std = [ "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [ + "ddc-primitives/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/pallets/ddc-clusters/src/benchmarking.rs b/pallets/ddc-clusters/src/benchmarking.rs new file mode 100644 index 000000000..9ce6b28d9 --- /dev/null +++ b/pallets/ddc-clusters/src/benchmarking.rs @@ -0,0 +1,119 @@ +//! DdcStaking pallet benchmarking. + +use ddc_primitives::{ClusterGovParams, ClusterId, ClusterParams, NodePubKey}; +pub use frame_benchmarking::{ + account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, + BenchmarkError, +}; +use frame_system::RawOrigin; +use pallet_contracts::chain_extension::UncheckedFrom; +use sp_runtime::{AccountId32, Perquintill}; +use sp_std::prelude::*; +use testing_utils::*; + +use super::*; +use crate::{cluster::ClusterProps, Pallet as DdcClusters}; + +const USER_SEED: u32 = 999666; +const USER_SEED_2: u32 = 999555; + +benchmarks! { + where_clause { where + T::AccountId: UncheckedFrom + AsRef<[u8]> } + + create_cluster { + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let cluster_params = ClusterParams { node_provider_auth_contract: Some(user.clone()) }; + let cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + cdn_bond_size: 100u32.into(), + cdn_chill_delay: 50u32.into(), + cdn_unbonding_delay: 50u32.into(), + storage_bond_size: 100u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + }: _(RawOrigin::Root, cluster_id, user.clone(), user, cluster_params, cluster_gov_params) + verify { + assert!(Clusters::::contains_key(cluster_id)); + } + + add_node { + let bytes = [0u8; 32]; + let node_pub_key = NodePubKey::CDNPubKey(AccountId32::from(bytes)); + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 1_000_000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let _ = config_cluster_and_node::(user.clone(), node_pub_key.clone(), cluster_id); + }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone()) + verify { + assert!(ClustersNodes::::contains_key(cluster_id, node_pub_key)); + } + + remove_node { + let bytes = [0u8; 32]; + let node_pub_key = NodePubKey::CDNPubKey(AccountId32::from(bytes)); + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 1_000_000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let _ = config_cluster_and_node::(user.clone(), node_pub_key.clone(), cluster_id); + let _ = DdcClusters::::add_node( + RawOrigin::Signed(user.clone()).into(), + cluster_id, + node_pub_key.clone() + ); + }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone()) + verify { + assert!(!ClustersNodes::::contains_key(cluster_id, node_pub_key)); + } + + set_cluster_params { + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let user_2 = account::("user", USER_SEED_2, 0u32); + let _ = config_cluster::(user.clone(), cluster_id); + let new_cluster_params = ClusterParams { node_provider_auth_contract: Some(user_2.clone()) }; + }: _(RawOrigin::Signed(user.clone()), cluster_id, new_cluster_params) + verify { + assert_eq!(Clusters::::try_get(cluster_id).unwrap().props, ClusterProps { node_provider_auth_contract: Some(user_2) }); + } + + set_cluster_gov_params { + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let _ = config_cluster::(user, cluster_id); + let new_cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + cdn_bond_size: 10u32.into(), + cdn_chill_delay: 5u32.into(), + cdn_unbonding_delay: 5u32.into(), + storage_bond_size: 10u32.into(), + storage_chill_delay: 5u32.into(), + storage_unbonding_delay: 5u32.into(), + unit_per_mb_stored: 1, + unit_per_mb_streamed: 1, + unit_per_put_request: 1, + unit_per_get_request: 1, + }; + }: _(RawOrigin::Root, cluster_id, new_cluster_gov_params.clone()) + verify { + assert_eq!(ClustersGovParams::::try_get(cluster_id).unwrap(), new_cluster_gov_params); + } + + impl_benchmark_test_suite!( + DdcClusters, + crate::mock::ExtBuilder.build(), + crate::mock::Test, + ); +} diff --git a/pallets/ddc-clusters/src/cluster.rs b/pallets/ddc-clusters/src/cluster.rs index ff55e49bf..5fc7821c8 100644 --- a/pallets/ddc-clusters/src/cluster.rs +++ b/pallets/ddc-clusters/src/cluster.rs @@ -1,14 +1,16 @@ use crate::pallet::Error; use codec::{Decode, Encode}; -use ddc_primitives::ClusterId; +use ddc_primitives::{ClusterId, ClusterParams}; use frame_support::{pallet_prelude::*, parameter_types}; use scale_info::TypeInfo; -use sp_runtime::Perquintill; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; parameter_types! { pub MaxClusterParamsLen: u16 = 2048; } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub struct Cluster { pub cluster_id: ClusterId, @@ -17,34 +19,10 @@ pub struct Cluster { pub props: ClusterProps, } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub struct ClusterProps { - pub node_provider_auth_contract: AccountId, -} - -// ClusterParams includes Governance non-sensetive parameters only -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -pub struct ClusterParams { - pub node_provider_auth_contract: AccountId, -} - -// ClusterGovParams includes Governance sensetive parameters -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -#[scale_info(skip_type_params(Balance, BlockNumber, T))] -pub struct ClusterGovParams { - pub treasury_share: Perquintill, - pub validators_share: Perquintill, - pub cluster_reserve_share: Perquintill, - pub cdn_bond_size: Balance, - pub cdn_chill_delay: BlockNumber, - pub cdn_unbonding_delay: BlockNumber, - pub storage_bond_size: Balance, - pub storage_chill_delay: BlockNumber, - pub storage_unbonding_delay: BlockNumber, - pub unit_per_mb_stored: u128, - pub unit_per_mb_streamed: u128, - pub unit_per_put_request: u128, - pub unit_per_get_request: u128, + pub node_provider_auth_contract: Option, } impl Cluster { diff --git a/pallets/ddc-clusters/src/lib.rs b/pallets/ddc-clusters/src/lib.rs index 99b8e1448..a9c501854 100644 --- a/pallets/ddc-clusters/src/lib.rs +++ b/pallets/ddc-clusters/src/lib.rs @@ -15,21 +15,33 @@ #![recursion_limit = "256"] #![feature(is_some_and)] // ToDo: delete at rustc > 1.70 +pub mod weights; +use crate::weights::WeightInfo; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; +#[cfg(any(feature = "runtime-benchmarks", test))] +pub mod testing_utils; + #[cfg(test)] pub(crate) mod mock; #[cfg(test)] mod tests; use crate::{ - cluster::{Cluster, ClusterGovParams, ClusterParams}, + cluster::Cluster, node_provider_auth::{NodeProviderAuthContract, NodeProviderAuthContractError}, }; -use ddc_primitives::{ClusterFeesParams, ClusterId, ClusterPricingParams, NodePubKey, NodeType}; +use ddc_primitives::{ + ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterId, ClusterParams, + ClusterPricingParams, NodePubKey, NodeType, +}; use ddc_traits::{ - cluster::{ClusterVisitor, ClusterVisitorError}, - staking::{StakingVisitor, StakingVisitorError}, + cluster::{ClusterCreator, ClusterVisitor, ClusterVisitorError}, + staking::{StakerCreator, StakingVisitor, StakingVisitorError}, }; use frame_support::{ + assert_ok, pallet_prelude::*, traits::{Currency, LockableCurrency}, }; @@ -39,7 +51,7 @@ use pallet_ddc_nodes::{NodeRepository, NodeTrait}; use sp_runtime::SaturatedConversion; use sp_std::prelude::*; -mod cluster; +pub mod cluster; mod node_provider_auth; /// The balance type of this pallet. @@ -49,6 +61,7 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { use super::*; + use ddc_traits::cluster::{ClusterManager, ClusterManagerError}; use pallet_contracts::chain_extension::UncheckedFrom; #[pallet::pallet] @@ -61,7 +74,9 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type NodeRepository: NodeRepository; // todo: get rid of tight coupling with nodes-pallet type StakingVisitor: StakingVisitor; + type StakerCreator: StakerCreator>; type Currency: LockableCurrency; + type WeightInfo: WeightInfo; } #[pallet::event] @@ -80,16 +95,18 @@ pub mod pallet { ClusterDoesNotExist, ClusterParamsExceedsLimit, AttemptToAddNonExistentNode, + AttemptToAddAlreadyAssignedNode, AttemptToRemoveNonExistentNode, - NodeIsAlreadyAssigned, - NodeIsNotAssigned, + AttemptToRemoveNotAssignedNode, OnlyClusterManager, NodeIsNotAuthorized, - NodeHasNoStake, + NodeHasNoActivatedStake, NodeStakeIsInvalid, /// Cluster candidate should not plan to chill. NodeChillingIsProhibited, NodeAuthContractCallFailed, + NodeAuthContractDeployFailed, + NodeAuthNodeAuthorizationNotSuccessful, } #[pallet::storage] @@ -114,12 +131,66 @@ pub mod pallet { OptionQuery, >; + #[pallet::genesis_config] + pub struct GenesisConfig { + pub clusters: Vec>, + #[allow(clippy::type_complexity)] + pub clusters_gov_params: Vec<(ClusterId, ClusterGovParams, T::BlockNumber>)>, + pub clusters_nodes: Vec<(ClusterId, Vec)>, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + clusters: Default::default(), + clusters_gov_params: Default::default(), + clusters_nodes: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig + where + T::AccountId: UncheckedFrom + AsRef<[u8]>, + { + fn build(&self) { + for cluster in &self.clusters { + assert_ok!(Pallet::::create_cluster( + frame_system::Origin::::Root.into(), + cluster.cluster_id, + cluster.manager_id.clone(), + cluster.reserve_id.clone(), + ClusterParams:: { + node_provider_auth_contract: cluster + .props + .node_provider_auth_contract + .clone(), + }, + self.clusters_gov_params + .iter() + .find(|(id, _)| id == &cluster.cluster_id) + .unwrap() + .1 + .clone(), + )); + + for (cluster_id, nodes) in &self.clusters_nodes { + for node_pub_key in nodes { + >::insert(cluster_id, node_pub_key, true); + } + } + } + } + } + #[pallet::call] impl Pallet where T::AccountId: UncheckedFrom + AsRef<[u8]>, { - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::create_cluster())] pub fn create_cluster( origin: OriginFor, cluster_id: ClusterId, @@ -129,19 +200,16 @@ pub mod pallet { cluster_gov_params: ClusterGovParams, T::BlockNumber>, ) -> DispatchResult { ensure_root(origin)?; // requires Governance approval - let cluster = - Cluster::new(cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params) - .map_err(Into::>::into)?; - ensure!(!Clusters::::contains_key(cluster_id), Error::::ClusterAlreadyExists); - - Clusters::::insert(cluster_id, cluster); - ClustersGovParams::::insert(cluster_id, cluster_gov_params); - Self::deposit_event(Event::::ClusterCreated { cluster_id }); - - Ok(()) + Self::do_create_cluster( + cluster_id, + cluster_manager_id, + cluster_reserve_id, + cluster_params, + cluster_gov_params, + ) } - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::add_node())] pub fn add_node( origin: OriginFor, cluster_id: ClusterId, @@ -150,47 +218,47 @@ pub mod pallet { let caller_id = ensure_signed(origin)?; let cluster = Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; - ensure!(cluster.manager_id == caller_id, Error::::OnlyClusterManager); - // Node with this node with this public key exists. - let mut node = T::NodeRepository::get(node_pub_key.clone()) - .map_err(|_| Error::::AttemptToAddNonExistentNode)?; - ensure!(node.get_cluster_id().is_none(), Error::::NodeIsAlreadyAssigned); + ensure!(cluster.manager_id == caller_id, Error::::OnlyClusterManager); // Sufficient funds are locked at the DDC Staking module. - let has_stake = T::StakingVisitor::node_has_stake(&node_pub_key, &cluster_id) - .map_err(Into::>::into)?; - ensure!(has_stake, Error::::NodeHasNoStake); + let has_activated_stake = + T::StakingVisitor::has_activated_stake(&node_pub_key, &cluster_id) + .map_err(Into::>::into)?; + ensure!(has_activated_stake, Error::::NodeHasNoActivatedStake); // Candidate is not planning to pause operations any time soon. - let is_chilling = T::StakingVisitor::node_is_chilling(&node_pub_key) + let has_chilling_attempt = T::StakingVisitor::has_chilling_attempt(&node_pub_key) .map_err(Into::>::into)?; - ensure!(!is_chilling, Error::::NodeChillingIsProhibited); + ensure!(!has_chilling_attempt, Error::::NodeChillingIsProhibited); + + // Node with this node with this public key exists. + let node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToAddNonExistentNode)?; // Cluster extension smart contract allows joining. - let auth_contract = NodeProviderAuthContract::::new( - cluster.props.node_provider_auth_contract, - caller_id, - ); - let is_authorized = auth_contract - .is_authorized( - node.get_provider_id().to_owned(), - node.get_pub_key(), - node.get_type(), - ) - .map_err(Into::>::into)?; - ensure!(is_authorized, Error::::NodeIsNotAuthorized); + if let Some(address) = cluster.props.node_provider_auth_contract { + let auth_contract = NodeProviderAuthContract::::new(address, caller_id); + + let is_authorized = auth_contract + .is_authorized( + node.get_provider_id().to_owned(), + node.get_pub_key(), + node.get_type(), + ) + .map_err(Into::>::into)?; + ensure!(is_authorized, Error::::NodeIsNotAuthorized); + }; // Add node to the cluster. - node.set_cluster_id(Some(cluster_id)); - T::NodeRepository::update(node).map_err(|_| Error::::AttemptToAddNonExistentNode)?; - ClustersNodes::::insert(cluster_id, node_pub_key.clone(), true); + >::add_node(&cluster_id, &node_pub_key) + .map_err(Into::>::into)?; Self::deposit_event(Event::::ClusterNodeAdded { cluster_id, node_pub_key }); Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::remove_node())] pub fn remove_node( origin: OriginFor, cluster_id: ClusterId, @@ -199,21 +267,19 @@ pub mod pallet { let caller_id = ensure_signed(origin)?; let cluster = Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + ensure!(cluster.manager_id == caller_id, Error::::OnlyClusterManager); - let mut node = T::NodeRepository::get(node_pub_key.clone()) - .map_err(|_| Error::::AttemptToRemoveNonExistentNode)?; - ensure!(node.get_cluster_id() == &Some(cluster_id), Error::::NodeIsNotAssigned); - node.set_cluster_id(None); - T::NodeRepository::update(node) - .map_err(|_| Error::::AttemptToRemoveNonExistentNode)?; - ClustersNodes::::remove(cluster_id, node_pub_key.clone()); + + // Remove node from the cluster. + >::remove_node(&cluster_id, &node_pub_key) + .map_err(Into::>::into)?; Self::deposit_event(Event::::ClusterNodeRemoved { cluster_id, node_pub_key }); Ok(()) } // Sets Governance non-sensetive parameters only - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::set_cluster_params())] pub fn set_cluster_params( origin: OriginFor, cluster_id: ClusterId, @@ -231,7 +297,7 @@ pub mod pallet { } // Requires Governance approval - #[pallet::weight(10_000)] + #[pallet::weight(::WeightInfo::set_cluster_gov_params())] pub fn set_cluster_gov_params( origin: OriginFor, cluster_id: ClusterId, @@ -247,11 +313,28 @@ pub mod pallet { } } - impl ClusterVisitor for Pallet { - fn cluster_has_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool { - ClustersNodes::::get(cluster_id, node_pub_key).is_some() + impl Pallet { + fn do_create_cluster( + cluster_id: ClusterId, + cluster_manager_id: T::AccountId, + cluster_reserve_id: T::AccountId, + cluster_params: ClusterParams, + cluster_gov_params: ClusterGovParams, T::BlockNumber>, + ) -> DispatchResult { + let cluster = + Cluster::new(cluster_id, cluster_manager_id, cluster_reserve_id, cluster_params) + .map_err(Into::>::into)?; + ensure!(!Clusters::::contains_key(cluster_id), Error::::ClusterAlreadyExists); + + Clusters::::insert(cluster_id, cluster); + ClustersGovParams::::insert(cluster_id, cluster_gov_params); + Self::deposit_event(Event::::ClusterCreated { cluster_id }); + + Ok(()) } + } + impl ClusterVisitor for Pallet { fn ensure_cluster(cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { Clusters::::get(cluster_id) .map(|_| ()) @@ -328,12 +411,96 @@ pub mod pallet { NodeType::CDN => Ok(cluster_gov_params.cdn_unbonding_delay), } } + + fn get_bonding_params( + cluster_id: &ClusterId, + ) -> Result, ClusterVisitorError> { + let cluster_gov_params = ClustersGovParams::::try_get(cluster_id) + .map_err(|_| ClusterVisitorError::ClusterGovParamsNotSet)?; + Ok(ClusterBondingParams { + cdn_bond_size: cluster_gov_params.cdn_bond_size.saturated_into::(), + cdn_chill_delay: cluster_gov_params.cdn_chill_delay, + cdn_unbonding_delay: cluster_gov_params.cdn_unbonding_delay, + storage_bond_size: cluster_gov_params.storage_bond_size.saturated_into::(), + storage_chill_delay: cluster_gov_params.storage_chill_delay, + storage_unbonding_delay: cluster_gov_params.storage_unbonding_delay, + }) + } + } + + impl ClusterManager for Pallet { + fn contains_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool { + ClustersNodes::::get(cluster_id, node_pub_key).is_some() + } + + fn add_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError> { + let mut node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| ClusterManagerError::AttemptToAddNonExistentNode)?; + + ensure!( + node.get_cluster_id().is_none(), + ClusterManagerError::AttemptToAddAlreadyAssignedNode + ); + + node.set_cluster_id(Some(*cluster_id)); + T::NodeRepository::update(node) + .map_err(|_| ClusterManagerError::AttemptToAddNonExistentNode)?; + + ClustersNodes::::insert(cluster_id, node_pub_key.clone(), true); + + Ok(()) + } + + fn remove_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError> { + let mut node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| ClusterManagerError::AttemptToRemoveNonExistentNode)?; + + ensure!( + node.get_cluster_id() == &Some(*cluster_id), + ClusterManagerError::AttemptToRemoveNotAssignedNode + ); + + node.set_cluster_id(None); + T::NodeRepository::update(node) + .map_err(|_| ClusterManagerError::AttemptToRemoveNonExistentNode)?; + + ClustersNodes::::remove(cluster_id, node_pub_key.clone()); + + Ok(()) + } + } + + impl ClusterCreator> for Pallet + where + T::AccountId: UncheckedFrom + AsRef<[u8]>, + { + fn create_new_cluster( + cluster_id: ClusterId, + cluster_manager_id: T::AccountId, + cluster_reserve_id: T::AccountId, + cluster_params: ClusterParams, + cluster_gov_params: ClusterGovParams, T::BlockNumber>, + ) -> DispatchResult { + Self::do_create_cluster( + cluster_id, + cluster_manager_id, + cluster_reserve_id, + cluster_params, + cluster_gov_params, + ) + } } impl From for Error { fn from(error: StakingVisitorError) -> Self { match error { - StakingVisitorError::NodeStakeDoesNotExist => Error::::NodeHasNoStake, + StakingVisitorError::NodeStakeDoesNotExist => Error::::NodeHasNoActivatedStake, StakingVisitorError::NodeStakeIsInBadState => Error::::NodeStakeIsInvalid, } } @@ -344,6 +511,25 @@ pub mod pallet { match error { NodeProviderAuthContractError::ContractCallFailed => Error::::NodeAuthContractCallFailed, + NodeProviderAuthContractError::ContractDeployFailed => + Error::::NodeAuthContractDeployFailed, + NodeProviderAuthContractError::NodeAuthorizationNotSuccessful => + Error::::NodeAuthNodeAuthorizationNotSuccessful, + } + } + } + + impl From for Error { + fn from(error: ClusterManagerError) -> Self { + match error { + ClusterManagerError::AttemptToRemoveNotAssignedNode => + Error::::AttemptToRemoveNotAssignedNode, + ClusterManagerError::AttemptToRemoveNonExistentNode => + Error::::AttemptToRemoveNonExistentNode, + ClusterManagerError::AttemptToAddNonExistentNode => + Error::::AttemptToAddNonExistentNode, + ClusterManagerError::AttemptToAddAlreadyAssignedNode => + Error::::AttemptToAddAlreadyAssignedNode, } } } diff --git a/pallets/ddc-clusters/src/mock.rs b/pallets/ddc-clusters/src/mock.rs index b6c2e4fd2..d9beb473d 100644 --- a/pallets/ddc-clusters/src/mock.rs +++ b/pallets/ddc-clusters/src/mock.rs @@ -4,7 +4,7 @@ use crate::{self as pallet_ddc_clusters, *}; use ddc_primitives::{ClusterId, NodePubKey}; -use ddc_traits::staking::{StakingVisitor, StakingVisitorError}; +use ddc_traits::staking::{StakerCreator, StakingVisitor, StakingVisitorError}; use frame_support::{ construct_runtime, parameter_types, traits::{ConstU32, ConstU64, Everything, Nothing}, @@ -19,7 +19,7 @@ use sp_runtime::{ traits::{ BlakeTwo256, Convert, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify, }, - MultiSignature, + MultiSignature, Perquintill, }; /// The AccountId alias in this test module. @@ -181,6 +181,8 @@ impl pallet_timestamp::Config for Test { impl pallet_ddc_nodes::Config for Test { type RuntimeEvent = RuntimeEvent; + type StakingVisitor = TestStakingVisitor; + type WeightInfo = (); } impl crate::pallet::Config for Test { @@ -188,27 +190,46 @@ impl crate::pallet::Config for Test { type Currency = Balances; type NodeRepository = DdcNodes; type StakingVisitor = TestStakingVisitor; + type StakerCreator = TestStaker; + type WeightInfo = (); } pub(crate) type DdcStakingCall = crate::Call; pub(crate) type TestRuntimeCall = ::RuntimeCall; pub struct TestStakingVisitor; +pub struct TestStaker; + impl StakingVisitor for TestStakingVisitor { - fn node_has_stake( + fn has_activated_stake( _node_pub_key: &NodePubKey, _cluster_id: &ClusterId, ) -> Result { Ok(true) } - fn node_is_chilling(_node_pub_key: &NodePubKey) -> Result { + fn has_stake(_node_pub_key: &NodePubKey) -> bool { + true + } + fn has_chilling_attempt(_node_pub_key: &NodePubKey) -> Result { Ok(false) } } +impl StakerCreator> for TestStaker { + fn bond_stake_and_participate( + _stash: T::AccountId, + _controller: T::AccountId, + _node: NodePubKey, + _value: BalanceOf, + _cluster_id: ClusterId, + ) -> sp_runtime::DispatchResult { + Ok(()) + } +} + pub struct ExtBuilder; impl ExtBuilder { - fn build(self) -> TestExternalities { + pub fn build(self) -> TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); @@ -222,6 +243,41 @@ impl ExtBuilder { } .assimilate_storage(&mut storage); + let cluster_gov_params = ClusterGovParams { + treasury_share: Perquintill::from_float(0.05), + validators_share: Perquintill::from_float(0.01), + cluster_reserve_share: Perquintill::from_float(0.02), + cdn_bond_size: 100, + cdn_chill_delay: 50, + cdn_unbonding_delay: 50, + storage_bond_size: 100, + storage_chill_delay: 50, + storage_unbonding_delay: 50, + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + + let node_pub_key = NodePubKey::CDNPubKey(AccountId::from([0; 32])); + + // For testing purposes only + pallet_ddc_clusters::GenesisConfig::::default().build(); + + if let Ok(cluster) = Cluster::new( + ClusterId::from([0; 20]), + AccountId::from([0; 32]), + AccountId::from([0; 32]), + ClusterParams { node_provider_auth_contract: Some(AccountId::from([0; 32])) }, + ) { + let _ = pallet_ddc_clusters::GenesisConfig:: { + clusters: vec![cluster], + clusters_gov_params: vec![(ClusterId::from([0; 20]), cluster_gov_params)], + clusters_nodes: vec![(ClusterId::from([0; 20]), vec![node_pub_key])], + } + .assimilate_storage(&mut storage); + } + TestExternalities::new(storage) } pub fn build_and_execute(self, test: impl FnOnce()) { diff --git a/pallets/ddc-clusters/src/node_provider_auth.rs b/pallets/ddc-clusters/src/node_provider_auth.rs index e3f038818..d73e45628 100644 --- a/pallets/ddc-clusters/src/node_provider_auth.rs +++ b/pallets/ddc-clusters/src/node_provider_auth.rs @@ -2,8 +2,10 @@ use crate::Config; use codec::Encode; use ddc_primitives::{NodePubKey, NodeType}; use frame_support::weights::Weight; +use hex_literal::hex; use pallet_contracts::chain_extension::UncheckedFrom; -use sp_std::prelude::Vec; +use sp_runtime::traits::Hash; +use sp_std::{prelude::Vec, vec}; /// ink! 4.x selector for the "is_authorized" message, equals to the first four bytes of the /// blake2("is_authorized"). See also: https://use.ink/basics/selectors#selector-calculation/, @@ -15,7 +17,7 @@ const INK_SELECTOR_IS_AUTHORIZED: [u8; 4] = [0x96, 0xb0, 0x45, 0x3e]; const EXTENSION_CALL_GAS_LIMIT: Weight = Weight::from_ref_time(5_000_000_000_000); pub struct NodeProviderAuthContract { - contract_id: T::AccountId, + pub contract_id: T::AccountId, caller_id: T::AccountId, } @@ -59,6 +61,72 @@ where Ok(is_authorized) } + pub fn deploy_contract( + &self, + caller_id: T::AccountId, + ) -> Result { + pub const CTOR_SELECTOR: [u8; 4] = hex!("9bae9d5e"); + + fn encode_constructor() -> Vec { + let mut call_data = CTOR_SELECTOR.to_vec(); + let x = 0_u128; + for _ in 0..9 { + x.encode_to(&mut call_data); + } + call_data + } + + // Load the contract code. + let wasm = &include_bytes!("./test_data/node_provider_auth_white_list.wasm")[..]; + let _wasm_hash = ::Hashing::hash(wasm); + let contract_args = encode_constructor(); + + // Deploy the contract. + let contract_id = pallet_contracts::Pallet::::bare_instantiate( + caller_id.clone(), + Default::default(), + EXTENSION_CALL_GAS_LIMIT, + None, + wasm.into(), + contract_args, + vec![], + false, + ) + .result + .map_err(|_| NodeProviderAuthContractError::ContractDeployFailed)? + .account_id; + + Ok(Self::new(contract_id, caller_id)) + } + + pub fn authorize_node( + &self, + node_pub_key: NodePubKey, + ) -> Result { + pub const ADD_DDC_NODE_SELECTOR: [u8; 4] = hex!("7a04093d"); + + let call_data = { + // is_authorized(node_provider: AccountId, node: Vec, node_variant: u8) -> bool + let args: ([u8; 4], Vec) = + (ADD_DDC_NODE_SELECTOR, node_pub_key.encode()[1..].to_vec()); + args.encode() + }; + + let _ = pallet_contracts::Pallet::::bare_call( + self.caller_id.clone(), + self.contract_id.clone(), + Default::default(), + EXTENSION_CALL_GAS_LIMIT, + None, + call_data, + false, + ) + .result + .map_err(|_| NodeProviderAuthContractError::NodeAuthorizationNotSuccessful)?; + + Ok(true) + } + pub fn new(contract_id: T::AccountId, caller_id: T::AccountId) -> Self { Self { contract_id, caller_id } } @@ -66,4 +134,6 @@ where pub enum NodeProviderAuthContractError { ContractCallFailed, + ContractDeployFailed, + NodeAuthorizationNotSuccessful, } diff --git a/pallets/ddc-clusters/src/testing_utils.rs b/pallets/ddc-clusters/src/testing_utils.rs new file mode 100644 index 000000000..b32e9ecca --- /dev/null +++ b/pallets/ddc-clusters/src/testing_utils.rs @@ -0,0 +1,133 @@ +//! DdcStaking pallet benchmarking. + +use ddc_primitives::{ + CDNNodeParams, ClusterGovParams, ClusterId, ClusterParams, NodeParams, NodePubKey, +}; +pub use frame_benchmarking::{ + account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, + BenchmarkError, +}; +use frame_system::RawOrigin; +use pallet_contracts::chain_extension::UncheckedFrom; +use pallet_ddc_nodes::Node; +use sp_runtime::Perquintill; +use sp_std::prelude::*; + +use crate::{Pallet as DdcClusters, *}; + +pub fn config_cluster(user: T::AccountId, cluster_id: ClusterId) +where + T::AccountId: UncheckedFrom + AsRef<[u8]>, +{ + let cluster_params = ClusterParams { node_provider_auth_contract: Some(user.clone()) }; + let cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + cdn_bond_size: 100u32.into(), + cdn_chill_delay: 50u32.into(), + cdn_unbonding_delay: 50u32.into(), + storage_bond_size: 100u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + + let _ = DdcClusters::::create_cluster( + RawOrigin::Root.into(), + cluster_id, + user.clone(), + user, + cluster_params, + cluster_gov_params, + ); +} + +pub fn config_cluster_and_node( + user: T::AccountId, + node_pub_key: NodePubKey, + cluster_id: ClusterId, +) -> Result<(), Box> +where + T::AccountId: UncheckedFrom + AsRef<[u8]>, +{ + let cluster_params = ClusterParams { node_provider_auth_contract: Some(user.clone()) }; + let cdn_node_params = CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + + let cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + cdn_bond_size: 100u32.into(), + cdn_chill_delay: 50u32.into(), + cdn_unbonding_delay: 50u32.into(), + storage_bond_size: 100u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + + let _ = DdcClusters::::create_cluster( + RawOrigin::Root.into(), + cluster_id, + user.clone(), + user.clone(), + cluster_params, + cluster_gov_params, + ); + + if let Ok(new_node) = + Node::::new(node_pub_key.clone(), user.clone(), NodeParams::CDNParams(cdn_node_params)) + { + let _ = T::NodeRepository::create(new_node); + } + + T::StakerCreator::bond_stake_and_participate( + user.clone(), + user.clone(), + node_pub_key.clone(), + 10_000u32.into(), + cluster_id, + ) + .unwrap(); + + let mut auth_contract = NodeProviderAuthContract::::new(user.clone(), user.clone()); + auth_contract = auth_contract.deploy_contract(user.clone())?; + auth_contract.authorize_node(node_pub_key)?; + + let updated_cluster_params = + ClusterParams { node_provider_auth_contract: Some(auth_contract.contract_id) }; + + // Register auth contract + let _ = DdcClusters::::set_cluster_params( + RawOrigin::Signed(user).into(), + cluster_id, + updated_cluster_params, + ); + + Ok(()) +} + +impl From for Box { + fn from(error: NodeProviderAuthContractError) -> Self { + match error { + NodeProviderAuthContractError::ContractCallFailed => + Box::new(BenchmarkError::Stop("NodeAuthContractCallFailed")), + NodeProviderAuthContractError::ContractDeployFailed => + Box::new(BenchmarkError::Stop("NodeAuthContractDeployFailed")), + NodeProviderAuthContractError::NodeAuthorizationNotSuccessful => + Box::new(BenchmarkError::Stop("NodeAuthNodeAuthorizationNotSuccessful")), + } + } +} diff --git a/pallets/ddc-clusters/src/tests.rs b/pallets/ddc-clusters/src/tests.rs index 9e87b2e49..b94627f47 100644 --- a/pallets/ddc-clusters/src/tests.rs +++ b/pallets/ddc-clusters/src/tests.rs @@ -1,18 +1,26 @@ //! Tests for the module. use super::{mock::*, *}; -use ddc_primitives::{ClusterId, NodePubKey}; +use ddc_primitives::{ + CDNNodeParams, ClusterBondingParams, ClusterFeesParams, ClusterId, ClusterParams, + ClusterPricingParams, NodeParams, NodePubKey, +}; +use ddc_traits::cluster::ClusterManager; use frame_support::{assert_noop, assert_ok, error::BadOrigin}; use frame_system::Config; use hex_literal::hex; -use pallet_ddc_nodes::{CDNNodeParams, NodeParams}; -use sp_runtime::{traits::Hash, AccountId32, Perquintill}; +use sp_runtime::{traits::Hash, Perquintill}; #[test] fn create_cluster_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract = AccountId::from([3; 32]); + let cluster_gov_params = ClusterGovParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), @@ -33,10 +41,10 @@ fn create_cluster_works() { assert_noop!( DdcClusters::create_cluster( RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - AccountId::from([1; 32]), - AccountId::from([2; 32]), - ClusterParams { node_provider_auth_contract: AccountId::from([1; 32]) }, + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { node_provider_auth_contract: Some(auth_contract.clone()) }, cluster_gov_params.clone() ), BadOrigin @@ -45,10 +53,10 @@ fn create_cluster_works() { // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( RuntimeOrigin::root(), - ClusterId::from([1; 20]), - AccountId::from([1; 32]), - AccountId::from([2; 32]), - ClusterParams { node_provider_auth_contract: AccountId::from([1; 32]) }, + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { node_provider_auth_contract: Some(auth_contract.clone()) }, cluster_gov_params.clone() )); @@ -56,20 +64,20 @@ fn create_cluster_works() { assert_noop!( DdcClusters::create_cluster( RuntimeOrigin::root(), - ClusterId::from([1; 20]), - AccountId::from([1; 32]), - AccountId::from([2; 32]), - ClusterParams { node_provider_auth_contract: AccountId::from([1; 32]) }, + cluster_id, + cluster_manager_id, + cluster_reserve_id, + ClusterParams { node_provider_auth_contract: Some(auth_contract) }, cluster_gov_params ), Error::::ClusterAlreadyExists ); + // Checking storage + assert!(Clusters::::contains_key(cluster_id)); // Checking that event was emitted assert_eq!(System::events().len(), 1); - System::assert_last_event( - Event::ClusterCreated { cluster_id: ClusterId::from([1; 20]) }.into(), - ) + System::assert_last_event(Event::ClusterCreated { cluster_id }.into()) }) } @@ -78,14 +86,19 @@ fn add_and_delete_node_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let node_pub_key = AccountId::from([3; 32]); + let contract_id = deploy_contract(); // Cluster doesn't exist assert_noop!( DdcClusters::add_node( - RuntimeOrigin::signed(AccountId::from([2; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), ), Error::::ClusterDoesNotExist ); @@ -93,10 +106,10 @@ fn add_and_delete_node_works() { // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( RuntimeOrigin::root(), - ClusterId::from([1; 20]), - AccountId::from([1; 32]), - AccountId::from([2; 32]), - ClusterParams { node_provider_auth_contract: AccountId::from([1; 32]) }, + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { node_provider_auth_contract: Some(cluster_manager_id.clone()) }, ClusterGovParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), @@ -117,9 +130,9 @@ fn add_and_delete_node_works() { // Not Cluster Manager assert_noop!( DdcClusters::add_node( - RuntimeOrigin::signed(AccountId::from([2; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_reserve_id), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), ), Error::::OnlyClusterManager ); @@ -127,17 +140,13 @@ fn add_and_delete_node_works() { // Node doesn't exist assert_noop!( DdcClusters::add_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), ), Error::::AttemptToAddNonExistentNode ); - // Create node - let bytes = [4u8; 32]; - let node_pub_key = AccountId32::from(bytes); - let cdn_node_params = CDNNodeParams { host: vec![1u8, 255], http_port: 35000u16, @@ -147,66 +156,71 @@ fn add_and_delete_node_works() { // Node created assert_ok!(DdcNodes::create_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - NodePubKey::CDNPubKey(node_pub_key), + RuntimeOrigin::signed(cluster_manager_id.clone()), + NodePubKey::CDNPubKey(node_pub_key.clone()), NodeParams::CDNParams(cdn_node_params) )); // Node doesn't exist assert_noop!( DdcClusters::add_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), ), Error::::NodeAuthContractCallFailed ); // Set the correct address for auth contract assert_ok!(DdcClusters::set_cluster_params( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - cluster::ClusterParams { node_provider_auth_contract: contract_id }, + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + ClusterParams { node_provider_auth_contract: Some(contract_id) }, )); - // Node doesn't exist + // Node added succesfully assert_ok!(DdcClusters::add_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), + )); + + assert!(>::contains_node( + &cluster_id, + &NodePubKey::CDNPubKey(node_pub_key.clone()) )); // Node already assigned assert_noop!( DdcClusters::add_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), ), - Error::::NodeIsAlreadyAssigned + Error::::AttemptToAddAlreadyAssignedNode ); // Checking that event was emitted System::assert_last_event( Event::ClusterNodeAdded { - cluster_id: ClusterId::from([1; 20]), - node_pub_key: NodePubKey::CDNPubKey(AccountId::from([4; 32])), + cluster_id, + node_pub_key: NodePubKey::CDNPubKey(node_pub_key.clone()), } .into(), ); // Remove node assert_ok!(DdcClusters::remove_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key.clone()), )); // Checking that event was emitted System::assert_last_event( Event::ClusterNodeRemoved { - cluster_id: ClusterId::from([1; 20]), - node_pub_key: NodePubKey::CDNPubKey(AccountId::from([4; 32])), + cluster_id, + node_pub_key: NodePubKey::CDNPubKey(node_pub_key.clone()), } .into(), ); @@ -214,11 +228,11 @@ fn add_and_delete_node_works() { // Remove node should fail assert_noop!( DdcClusters::remove_node( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - NodePubKey::CDNPubKey(AccountId::from([4; 32])), + RuntimeOrigin::signed(cluster_manager_id), + cluster_id, + NodePubKey::CDNPubKey(node_pub_key), ), - Error::::NodeIsNotAssigned + Error::::AttemptToRemoveNotAssignedNode ); pub const CTOR_SELECTOR: [u8; 4] = hex!("9bae9d5e"); @@ -233,8 +247,10 @@ fn add_and_delete_node_works() { } fn deploy_contract() -> AccountId { + let cluster_manager_id = AccountId::from([1; 32]); + let node_pub_key = AccountId::from([3; 32]); // Admin account who deploys the contract. - let alice = AccountId::from([1; 32]); + let alice = cluster_manager_id; let _ = Balances::deposit_creating(&alice, 1_000_000_000_000); // Load the contract code. @@ -261,7 +277,7 @@ fn add_and_delete_node_works() { let contract_id = Contracts::contract_address(&alice, &wasm_hash, &[]); pub const ADD_DDC_NODE_SELECTOR: [u8; 4] = hex!("7a04093d"); - let node_pub_key = NodePubKey::CDNPubKey(AccountId::from([4; 32])); + let node_pub_key = NodePubKey::CDNPubKey(node_pub_key); let call_data = { // is_authorized(node_provider: AccountId, node: Vec, node_variant: u8) -> bool @@ -291,12 +307,18 @@ fn set_cluster_params_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract_1 = AccountId::from([3; 32]); + let auth_contract_2 = AccountId::from([4; 32]); + // Cluster doesn't exist assert_noop!( DdcClusters::set_cluster_params( - RuntimeOrigin::signed(AccountId::from([2; 32])), - ClusterId::from([2; 20]), - ClusterParams { node_provider_auth_contract: AccountId::from([2; 32]) }, + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + ClusterParams { node_provider_auth_contract: Some(auth_contract_1.clone()) }, ), Error::::ClusterDoesNotExist ); @@ -304,10 +326,10 @@ fn set_cluster_params_works() { // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( RuntimeOrigin::root(), - ClusterId::from([1; 20]), - AccountId::from([1; 32]), - AccountId::from([2; 32]), - ClusterParams { node_provider_auth_contract: AccountId::from([1; 32]) }, + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id.clone(), + ClusterParams { node_provider_auth_contract: Some(auth_contract_1) }, ClusterGovParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), @@ -327,24 +349,22 @@ fn set_cluster_params_works() { assert_noop!( DdcClusters::set_cluster_params( - RuntimeOrigin::signed(AccountId::from([2; 32])), - ClusterId::from([1; 20]), - ClusterParams { node_provider_auth_contract: AccountId::from([2; 32]) }, + RuntimeOrigin::signed(cluster_reserve_id), + cluster_id, + ClusterParams { node_provider_auth_contract: Some(auth_contract_2.clone()) }, ), Error::::OnlyClusterManager ); assert_ok!(DdcClusters::set_cluster_params( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), - ClusterParams { node_provider_auth_contract: AccountId::from([2; 32]) }, + RuntimeOrigin::signed(cluster_manager_id), + cluster_id, + ClusterParams { node_provider_auth_contract: Some(auth_contract_2) }, )); // Checking that event was emitted assert_eq!(System::events().len(), 2); - System::assert_last_event( - Event::ClusterParamsSet { cluster_id: ClusterId::from([1; 20]) }.into(), - ) + System::assert_last_event(Event::ClusterParamsSet { cluster_id }.into()) }) } @@ -353,6 +373,11 @@ fn set_cluster_gov_params_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract = AccountId::from([3; 32]); + let cluster_gov_params = ClusterGovParams { treasury_share: Perquintill::from_float(0.05), validators_share: Perquintill::from_float(0.01), @@ -373,7 +398,7 @@ fn set_cluster_gov_params_works() { assert_noop!( DdcClusters::set_cluster_gov_params( RuntimeOrigin::root(), - ClusterId::from([2; 20]), + cluster_id, cluster_gov_params.clone() ), Error::::ClusterDoesNotExist @@ -381,17 +406,17 @@ fn set_cluster_gov_params_works() { assert_ok!(DdcClusters::create_cluster( RuntimeOrigin::root(), - ClusterId::from([1; 20]), - AccountId::from([1; 32]), - AccountId::from([2; 32]), - ClusterParams { node_provider_auth_contract: AccountId::from([1; 32]) }, + cluster_id, + cluster_manager_id.clone(), + cluster_reserve_id, + ClusterParams { node_provider_auth_contract: Some(auth_contract) }, cluster_gov_params.clone() )); assert_noop!( DdcClusters::set_cluster_gov_params( - RuntimeOrigin::signed(AccountId::from([1; 32])), - ClusterId::from([1; 20]), + RuntimeOrigin::signed(cluster_manager_id), + cluster_id, cluster_gov_params.clone() ), BadOrigin @@ -399,14 +424,163 @@ fn set_cluster_gov_params_works() { assert_ok!(DdcClusters::set_cluster_gov_params( RuntimeOrigin::root(), - ClusterId::from([1; 20]), + cluster_id, cluster_gov_params )); // Checking that event was emitted assert_eq!(System::events().len(), 2); - System::assert_last_event( - Event::ClusterGovParamsSet { cluster_id: ClusterId::from([1; 20]) }.into(), - ) + System::assert_last_event(Event::ClusterGovParamsSet { cluster_id }.into()) + }) +} + +#[test] +fn cluster_visitor_works() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract = AccountId::from([3; 32]); + + let cluster_gov_params = ClusterGovParams { + treasury_share: Perquintill::from_float(0.05), + validators_share: Perquintill::from_float(0.01), + cluster_reserve_share: Perquintill::from_float(0.02), + cdn_bond_size: 100, + cdn_chill_delay: 50, + cdn_unbonding_delay: 50, + storage_bond_size: 100, + storage_chill_delay: 50, + storage_unbonding_delay: 50, + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + + // Creating 1 cluster should work fine + assert_ok!(DdcClusters::create_cluster( + RuntimeOrigin::root(), + cluster_id, + cluster_manager_id, + cluster_reserve_id.clone(), + ClusterParams { node_provider_auth_contract: Some(auth_contract) }, + cluster_gov_params + )); + + assert_ok!(>::ensure_cluster(&cluster_id)); + + assert_eq!( + >::get_bond_size(&cluster_id, NodeType::CDN) + .unwrap(), + 100u128 + ); + assert_eq!( + >::get_bond_size(&cluster_id, NodeType::Storage) + .unwrap(), + 100u128 + ); + + assert_eq!( + >::get_pricing_params(&cluster_id).unwrap(), + ClusterPricingParams { + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + } + ); + + assert_eq!( + >::get_fees_params(&cluster_id).unwrap(), + ClusterFeesParams { + treasury_share: Perquintill::from_float(0.05), + validators_share: Perquintill::from_float(0.01), + cluster_reserve_share: Perquintill::from_float(0.02) + } + ); + + assert_eq!( + >::get_reserve_account_id(&cluster_id).unwrap(), + cluster_reserve_id + ); + + assert_eq!( + >::get_chill_delay(&cluster_id, NodeType::CDN) + .unwrap(), + 50 + ); + assert_eq!( + >::get_chill_delay(&cluster_id, NodeType::Storage) + .unwrap(), + 50 + ); + + assert_eq!( + >::get_unbonding_delay(&cluster_id, NodeType::CDN) + .unwrap(), + 50 + ); + assert_eq!( + >::get_unbonding_delay( + &cluster_id, + NodeType::Storage + ) + .unwrap(), + 50 + ); + + assert_eq!( + >::get_bonding_params(&cluster_id).unwrap(), + ClusterBondingParams::<::BlockNumber> { + cdn_bond_size: 100, + cdn_chill_delay: 50, + cdn_unbonding_delay: 50, + storage_bond_size: 100, + storage_chill_delay: 50, + storage_unbonding_delay: 50, + } + ); + }) +} + +#[test] +fn cluster_creator_works() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + + let cluster_id = ClusterId::from([1; 20]); + let cluster_manager_id = AccountId::from([1; 32]); + let cluster_reserve_id = AccountId::from([2; 32]); + let auth_contract = AccountId::from([3; 32]); + + let cluster_gov_params = ClusterGovParams { + treasury_share: Perquintill::from_float(0.05), + validators_share: Perquintill::from_float(0.01), + cluster_reserve_share: Perquintill::from_float(0.02), + cdn_bond_size: 100, + cdn_chill_delay: 50, + cdn_unbonding_delay: 50, + storage_bond_size: 100, + storage_chill_delay: 50, + storage_unbonding_delay: 50, + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + + assert_ok!(>>::create_new_cluster( + cluster_id, + cluster_manager_id, + cluster_reserve_id, + ClusterParams { node_provider_auth_contract: Some(auth_contract) }, + cluster_gov_params + )); + + assert!(Clusters::::contains_key(cluster_id)); + assert!(ClustersGovParams::::contains_key(cluster_id)); }) } diff --git a/pallets/ddc-clusters/src/weights.rs b/pallets/ddc-clusters/src/weights.rs new file mode 100644 index 000000000..96e4b735f --- /dev/null +++ b/pallets/ddc-clusters/src/weights.rs @@ -0,0 +1,155 @@ + +//! Autogenerated weights for `pallet_ddc_clusters` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-16, STEPS: `200`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Raids-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: None, WASM-EXECUTION: Interpreted, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/cere +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_ddc_clusters +// --extrinsic +// * +// --steps +// 20 +// --repeat +// 50 +// --output +// pallets/ddc-clusters/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_ddc_clusters. +pub trait WeightInfo { + fn create_cluster() -> Weight; + fn add_node() -> Weight; + fn remove_node() -> Weight; + fn set_cluster_params() -> Weight; + fn set_cluster_gov_params() -> Weight; +} + +/// Weight functions for `pallet_ddc_clusters`. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: DdcClusters Clusters (r:1 w:1) + // Storage: DdcClusters ClustersGovParams (r:0 w:1) + fn create_cluster() -> Weight { + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_000_000u64) + .saturating_add(T::DbWeight::get().reads(1u64)) + .saturating_add(T::DbWeight::get().writes(2u64)) + } + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcNodes CDNNodes (r:1 w:1) + // Storage: DdcStaking Nodes (r:1 w:0) + // Storage: DdcStaking CDNs (r:1 w:0) + // Storage: DdcStaking Storages (r:1 w:0) + // Storage: DdcStaking Bonded (r:1 w:0) + // Storage: DdcStaking Ledger (r:1 w:0) + // Storage: System Account (r:1 w:0) + // Storage: Contracts ContractInfoOf (r:1 w:1) + // Storage: Contracts CodeStorage (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: System EventTopics (r:2 w:2) + // Storage: DdcClusters ClustersNodes (r:0 w:1) + // Storage: unknown [0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3] (r:1 w:0) + // Storage: unknown [0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957] (r:1 w:0) + fn add_node() -> Weight { + // Minimum execution time: 307_000 nanoseconds. + Weight::from_ref_time(354_000_000u64) + .saturating_add(T::DbWeight::get().reads(15u64)) + .saturating_add(T::DbWeight::get().writes(5u64)) + } + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcNodes CDNNodes (r:1 w:1) + // Storage: DdcClusters ClustersNodes (r:0 w:1) + fn remove_node() -> Weight { + // Minimum execution time: 23_000 nanoseconds. + Weight::from_ref_time(24_000_000u64) + .saturating_add(T::DbWeight::get().reads(2u64)) + .saturating_add(T::DbWeight::get().writes(2u64)) + } + // Storage: DdcClusters Clusters (r:1 w:1) + fn set_cluster_params() -> Weight { + // Minimum execution time: 15_000 nanoseconds. + Weight::from_ref_time(16_000_000u64) + .saturating_add(T::DbWeight::get().reads(1u64)) + .saturating_add(T::DbWeight::get().writes(1u64)) + } + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:0 w:1) + fn set_cluster_gov_params() -> Weight { + // Minimum execution time: 15_000 nanoseconds. + Weight::from_ref_time(16_000_000u64) + .saturating_add(T::DbWeight::get().reads(1u64)) + .saturating_add(T::DbWeight::get().writes(1u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: DdcClusters Clusters (r:1 w:1) + // Storage: DdcClusters ClustersGovParams (r:0 w:1) + fn create_cluster() -> Weight { + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_000_000u64) + .saturating_add(RocksDbWeight::get().reads(1u64)) + .saturating_add(RocksDbWeight::get().writes(2u64)) + } + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcNodes CDNNodes (r:1 w:1) + // Storage: DdcStaking Nodes (r:1 w:0) + // Storage: DdcStaking CDNs (r:1 w:0) + // Storage: DdcStaking Storages (r:1 w:0) + // Storage: DdcStaking Bonded (r:1 w:0) + // Storage: DdcStaking Ledger (r:1 w:0) + // Storage: System Account (r:1 w:0) + // Storage: Contracts ContractInfoOf (r:1 w:1) + // Storage: Contracts CodeStorage (r:1 w:0) + // Storage: Timestamp Now (r:1 w:0) + // Storage: System EventTopics (r:2 w:2) + // Storage: DdcClusters ClustersNodes (r:0 w:1) + // Storage: unknown [0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3] (r:1 w:0) + // Storage: unknown [0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957] (r:1 w:0) + fn add_node() -> Weight { + // Minimum execution time: 307_000 nanoseconds. + Weight::from_ref_time(354_000_000u64) + .saturating_add(RocksDbWeight::get().reads(15u64)) + .saturating_add(RocksDbWeight::get().writes(5u64)) + } + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcNodes CDNNodes (r:1 w:1) + // Storage: DdcClusters ClustersNodes (r:0 w:1) + fn remove_node() -> Weight { + // Minimum execution time: 23_000 nanoseconds. + Weight::from_ref_time(24_000_000u64) + .saturating_add(RocksDbWeight::get().reads(2u64)) + .saturating_add(RocksDbWeight::get().writes(2u64)) + } + // Storage: DdcClusters Clusters (r:1 w:1) + fn set_cluster_params() -> Weight { + // Minimum execution time: 15_000 nanoseconds. + Weight::from_ref_time(16_000_000u64) + .saturating_add(RocksDbWeight::get().reads(1u64)) + .saturating_add(RocksDbWeight::get().writes(1u64)) + } + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:0 w:1) + fn set_cluster_gov_params() -> Weight { + // Minimum execution time: 15_000 nanoseconds. + Weight::from_ref_time(16_000_000u64) + .saturating_add(RocksDbWeight::get().reads(1u64)) + .saturating_add(RocksDbWeight::get().writes(1u64)) + } +} \ No newline at end of file diff --git a/pallets/ddc-customers/Cargo.toml b/pallets/ddc-customers/Cargo.toml index b7191864e..93f1c2ddd 100644 --- a/pallets/ddc-customers/Cargo.toml +++ b/pallets/ddc-customers/Cargo.toml @@ -4,21 +4,29 @@ version = "0.1.0" edition = "2021" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -ddc-primitives = { version = "0.1.0", default-features = false, path = "../../primitives" } -ddc-traits = { version = "0.1.0", default-features = false, path = "../../traits" } +frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", optional = true } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } -log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } +# 3rd Party +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4.17", default-features = false } +rand_chacha = { version = "0.2", default-features = false, optional = true } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Cere +ddc-primitives = { version = "0.1.0", default-features = false, path = "../../primitives" } +ddc-traits = { version = "0.1.0", default-features = false, path = "../../traits" } + [dev-dependencies] +frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } pallet-timestamp = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } +sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-tracing = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } substrate-test-utils = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } @@ -29,7 +37,15 @@ std = [ "ddc-primitives/std", "frame-support/std", "frame-system/std", + "frame-benchmarking?/std", "scale-info/std", "sp-runtime/std", "sp-std/std", ] +runtime-benchmarks = [ + "ddc-primitives/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/pallets/ddc-customers/src/benchmarking.rs b/pallets/ddc-customers/src/benchmarking.rs new file mode 100644 index 000000000..84d16daec --- /dev/null +++ b/pallets/ddc-customers/src/benchmarking.rs @@ -0,0 +1,153 @@ +//! DdcStaking pallet benchmarking. +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as DdcCustomers; +use ddc_primitives::{ClusterGovParams, ClusterId, ClusterParams}; +use frame_benchmarking::{account, benchmarks, whitelist_account}; +use frame_support::traits::Currency; +use sp_runtime::Perquintill; +use sp_std::prelude::*; + +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +use frame_system::{Pallet as System, RawOrigin}; + +const USER_SEED: u32 = 999666; + +benchmarks! { + create_bucket { + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + + let cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + cdn_bond_size: 100u32.into(), + cdn_chill_delay: 50u32.into(), + cdn_unbonding_delay: 50u32.into(), + storage_bond_size: 100u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + + let _ = ::ClusterCreator::create_new_cluster( + ClusterId::from([1; 20]), + user.clone(), + user.clone(), + ClusterParams { node_provider_auth_contract: Some(user.clone()) }, + cluster_gov_params + ); + + whitelist_account!(user); + }: _(RawOrigin::Signed(user), cluster_id) + verify { + assert_eq!(Pallet::::buckets_count(), 1); + } + + deposit { + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 100u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let amount = ::Currency::minimum_balance() * 50u32.into(); + + whitelist_account!(user); + }: _(RawOrigin::Signed(user.clone()), amount) + verify { + assert!(Ledger::::contains_key(user)); + } + + deposit_extra { + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 200u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let amount = ::Currency::minimum_balance() * 50u32.into(); + + let _ = DdcCustomers::::deposit(RawOrigin::Signed(user.clone()).into(), amount); + + whitelist_account!(user); + }: _(RawOrigin::Signed(user.clone()), amount) + verify { + assert!(Ledger::::contains_key(user)); + } + + unlock_deposit { + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 200u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let amount = ::Currency::minimum_balance() * 50u32.into(); + + let _ = DdcCustomers::::deposit(RawOrigin::Signed(user.clone()).into(), amount); + + whitelist_account!(user); + }: unlock_deposit(RawOrigin::Signed(user.clone()), amount) + verify { + assert!(Ledger::::contains_key(user)); + } + + // Worst case scenario, 31/32 chunks unlocked after the unlocking duration + withdraw_unlocked_deposit_update { + + System::::set_block_number(1u32.into()); + + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 2000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let amount = ::Currency::minimum_balance() * 32u32.into(); + + let _ = DdcCustomers::::deposit(RawOrigin::Signed(user.clone()).into(), amount); + + for _k in 1 .. 32 { + let _ = DdcCustomers::::unlock_deposit(RawOrigin::Signed(user.clone()).into(), ::Currency::minimum_balance() * 1u32.into()); + } + + System::::set_block_number(5256001u32.into()); + + whitelist_account!(user); + }: withdraw_unlocked_deposit(RawOrigin::Signed(user.clone())) + verify { + let ledger = Ledger::::try_get(user).unwrap(); + assert_eq!(ledger.active, amount / 32u32.into()); + } + + // Worst case scenario, everything is removed after the unlocking duration + withdraw_unlocked_deposit_kill { + + System::::set_block_number(1u32.into()); + + let user = account::("user", USER_SEED, 0u32); + let user2 = account::("user", USER_SEED, 1u32); + let balance = ::Currency::minimum_balance() * 2000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let _ = ::Currency::make_free_balance_be(&user2, balance); + let amount = ::Currency::minimum_balance() * 32u32.into(); + + let _ = DdcCustomers::::deposit(RawOrigin::Signed(user.clone()).into(), amount); + // To keep the balance of pallet positive + let _ = DdcCustomers::::deposit(RawOrigin::Signed(user2).into(), amount); + + + for _k in 1 .. 33 { + let _ = DdcCustomers::::unlock_deposit(RawOrigin::Signed(user.clone()).into(), ::Currency::minimum_balance() * 1u32.into()); + } + + System::::set_block_number(5256001u32.into()); + + whitelist_account!(user); + }: withdraw_unlocked_deposit(RawOrigin::Signed(user.clone())) + verify { + assert!(!Ledger::::contains_key(user)); + } + + impl_benchmark_test_suite!( + DdcCustomers, + crate::mock::ExtBuilder.build(), + crate::mock::Test, + ); +} diff --git a/pallets/ddc-customers/src/lib.rs b/pallets/ddc-customers/src/lib.rs index 90e5c3939..3c3d72d69 100644 --- a/pallets/ddc-customers/src/lib.rs +++ b/pallets/ddc-customers/src/lib.rs @@ -1,6 +1,12 @@ #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] +pub mod weights; +use crate::weights::WeightInfo; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + #[cfg(test)] pub(crate) mod mock; #[cfg(test)] @@ -9,7 +15,10 @@ mod tests; use codec::{Decode, Encode, HasCompact}; use ddc_primitives::{BucketId, ClusterId}; -use ddc_traits::{cluster::ClusterVisitor, customer::CustomerCharger}; +use ddc_traits::{ + cluster::{ClusterCreator, ClusterVisitor}, + customer::{CustomerCharger, CustomerDepositor}, +}; use frame_support::{ parameter_types, traits::{Currency, DefensiveSaturating, ExistenceRequirement}, @@ -132,6 +141,8 @@ pub mod pallet { #[pallet::constant] type UnlockingDelay: Get<::BlockNumber>; type ClusterVisitor: ClusterVisitor; + type ClusterCreator: ClusterCreator>; + type WeightInfo: WeightInfo; } /// Map from all (unlocked) "owner" accounts to the info regarding the staking. @@ -200,6 +211,8 @@ pub mod pallet { ArithmeticOverflow, // Arithmetic underflow ArithmeticUnderflow, + // Transferring balance to pallet's vault has failed + TransferFailed, } #[pallet::genesis_config] @@ -222,7 +235,7 @@ pub mod pallet { /// Create new bucket with specified cluster id /// /// Anyone can create a bucket - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::create_bucket())] pub fn create_bucket(origin: OriginFor, cluster_id: ClusterId) -> DispatchResult { let bucket_owner = ensure_signed(origin)?; let cur_bucket_id = @@ -249,34 +262,13 @@ pub mod pallet { /// The dispatch origin for this call must be _Signed_ by the owner account. /// /// Emits `Deposited`. - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::deposit())] pub fn deposit( origin: OriginFor, #[pallet::compact] value: BalanceOf, ) -> DispatchResult { let owner = ensure_signed(origin)?; - - if >::contains_key(&owner) { - Err(Error::::AlreadyPaired)? - } - - // Reject a deposit which is considered to be _dust_. - if value < ::Currency::minimum_balance() { - Err(Error::::InsufficientDeposit)? - } - - frame_system::Pallet::::inc_consumers(&owner).map_err(|_| Error::::BadState)?; - - let owner_balance = ::Currency::free_balance(&owner); - let value = value.min(owner_balance); - let item = AccountsLedger { - owner: owner.clone(), - total: value, - active: value, - unlocking: Default::default(), - }; - Self::update_ledger_and_deposit(&owner, &item)?; - Self::deposit_event(Event::::Deposited(owner, value)); + >::deposit(owner, value.saturated_into())?; Ok(()) } @@ -286,32 +278,13 @@ pub mod pallet { /// The dispatch origin for this call must be _Signed_ by the owner. /// /// Emits `Deposited`. - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::deposit_extra())] pub fn deposit_extra( origin: OriginFor, #[pallet::compact] max_additional: BalanceOf, ) -> DispatchResult { let owner = ensure_signed(origin)?; - - let mut ledger = Self::ledger(&owner).ok_or(Error::::NotOwner)?; - - let owner_balance = ::Currency::free_balance(&owner); - let extra = owner_balance.min(max_additional); - ledger.total = - ledger.total.checked_add(&extra).ok_or(Error::::ArithmeticOverflow)?; - ledger.active = - ledger.active.checked_add(&extra).ok_or(Error::::ArithmeticOverflow)?; - - // Last check: the new active amount of ledger must be more than ED. - ensure!( - ledger.active >= ::Currency::minimum_balance(), - Error::::InsufficientDeposit - ); - - Self::update_ledger_and_deposit(&owner, &ledger)?; - - Self::deposit_event(Event::::Deposited(owner, extra)); - + >::deposit_extra(owner, max_additional.saturated_into())?; Ok(()) } @@ -331,7 +304,7 @@ pub mod pallet { /// Emits `InitialDepositUnlock`. /// /// See also [`Call::withdraw_unlocked_deposit`]. - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::unlock_deposit())] pub fn unlock_deposit( origin: OriginFor, #[pallet::compact] value: BalanceOf, @@ -392,15 +365,15 @@ pub mod pallet { /// Emits `Withdrawn`. /// /// See also [`Call::unlock_deposit`]. - #[pallet::weight(10_000)] - pub fn withdraw_unlocked_deposit(origin: OriginFor) -> DispatchResult { + #[pallet::weight(T::WeightInfo::withdraw_unlocked_deposit_kill())] + pub fn withdraw_unlocked_deposit(origin: OriginFor) -> DispatchResultWithPostInfo { let owner = ensure_signed(origin)?; let mut ledger = Self::ledger(&owner).ok_or(Error::::NotOwner)?; let (owner, old_total) = (ledger.owner.clone(), ledger.total); let current_block = >::block_number(); ledger = ledger.consolidate_unlocked(current_block); - if ledger.unlocking.is_empty() && + let post_info_weight = if ledger.unlocking.is_empty() && ledger.active < ::Currency::minimum_balance() { log::debug!("Killing owner"); @@ -408,11 +381,15 @@ pub mod pallet { // active portion to fall below existential deposit + will have no more unlocking // chunks left. We can now safely remove all accounts-related information. Self::kill_owner(&owner)?; + // This is worst case scenario, so we use the full weight and return None + None } else { log::debug!("Updating ledger"); // This was the consequence of a partial deposit unlock. just update the ledger and // move on. >::insert(&owner, &ledger); + // This is only an update, so we use less overall weight. + Some(::WeightInfo::withdraw_unlocked_deposit_update()) }; log::debug!("Current total: {:?}", ledger.total); @@ -435,7 +412,7 @@ pub mod pallet { Self::deposit_event(Event::::Withdrawn(owner, value)); } - Ok(()) + Ok(post_info_weight.into()) } } @@ -571,4 +548,60 @@ pub mod pallet { Ok(actually_charged.saturated_into::()) } } + + impl CustomerDepositor for Pallet { + fn deposit(owner: T::AccountId, amount: u128) -> Result<(), DispatchError> { + let value = amount.saturated_into::>(); + + if >::contains_key(&owner) { + Err(Error::::AlreadyPaired)? + } + + // Reject a deposit which is considered to be _dust_. + if value < ::Currency::minimum_balance() { + Err(Error::::InsufficientDeposit)? + } + + frame_system::Pallet::::inc_consumers(&owner).map_err(|_| Error::::BadState)?; + + let owner_balance = ::Currency::free_balance(&owner); + let value = value.min(owner_balance); + let item = AccountsLedger { + owner: owner.clone(), + total: value, + active: value, + unlocking: Default::default(), + }; + + Self::update_ledger_and_deposit(&owner, &item) + .map_err(|_| Error::::TransferFailed)?; + Self::deposit_event(Event::::Deposited(owner, value)); + + Ok(()) + } + + fn deposit_extra(owner: T::AccountId, amount: u128) -> Result<(), DispatchError> { + let max_additional = amount.saturated_into::>(); + let mut ledger = Self::ledger(&owner).ok_or(Error::::NotOwner)?; + + let owner_balance = ::Currency::free_balance(&owner); + let extra = owner_balance.min(max_additional); + ledger.total = + ledger.total.checked_add(&extra).ok_or(Error::::ArithmeticOverflow)?; + ledger.active = + ledger.active.checked_add(&extra).ok_or(Error::::ArithmeticOverflow)?; + + // Last check: the new active amount of ledger must be more than ED. + ensure!( + ledger.active >= ::Currency::minimum_balance(), + Error::::InsufficientDeposit + ); + + Self::update_ledger_and_deposit(&owner, &ledger) + .map_err(|_| Error::::TransferFailed)?; + Self::deposit_event(Event::::Deposited(owner, extra)); + + Ok(()) + } + } } diff --git a/pallets/ddc-customers/src/mock.rs b/pallets/ddc-customers/src/mock.rs index 43db67e76..11af9b667 100644 --- a/pallets/ddc-customers/src/mock.rs +++ b/pallets/ddc-customers/src/mock.rs @@ -1,8 +1,13 @@ //! Test utilities use crate::{self as pallet_ddc_customers, *}; -use ddc_primitives::{ClusterFeesParams, ClusterPricingParams, NodePubKey, NodeType}; -use ddc_traits::cluster::{ClusterVisitor, ClusterVisitorError}; +use ddc_primitives::{ + ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterId, ClusterParams, + ClusterPricingParams, NodePubKey, NodeType, +}; +use ddc_traits::cluster::{ + ClusterCreator, ClusterManager, ClusterManagerError, ClusterVisitor, ClusterVisitorError, +}; use frame_support::{ construct_runtime, parameter_types, @@ -15,7 +20,7 @@ use sp_io::TestExternalities; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, - Perquintill, + DispatchResult, Perquintill, }; /// The AccountId alias in this test module. @@ -101,13 +106,12 @@ impl crate::pallet::Config for Test { type PalletId = DdcCustomersPalletId; type RuntimeEvent = RuntimeEvent; type ClusterVisitor = TestClusterVisitor; + type ClusterCreator = TestClusterCreator; + type WeightInfo = (); } pub struct TestClusterVisitor; impl ClusterVisitor for TestClusterVisitor { - fn cluster_has_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { - true - } fn ensure_cluster(_cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { Ok(()) } @@ -154,12 +158,83 @@ impl ClusterVisitor for TestClusterVisitor { ) -> Result { Err(ClusterVisitorError::ClusterDoesNotExist) } + + fn get_bonding_params( + cluster_id: &ClusterId, + ) -> Result, ClusterVisitorError> { + Ok(ClusterBondingParams { + cdn_bond_size: >::get_bond_size( + cluster_id, + NodeType::CDN, + ) + .unwrap_or_default(), + cdn_chill_delay: >::get_chill_delay( + cluster_id, + NodeType::CDN, + ) + .unwrap_or_default(), + cdn_unbonding_delay: >::get_unbonding_delay( + cluster_id, + NodeType::CDN, + ) + .unwrap_or_default(), + storage_bond_size: >::get_bond_size( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + storage_chill_delay: >::get_chill_delay( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + storage_unbonding_delay: + >::get_unbonding_delay( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + }) + } +} + +pub struct TestClusterManager; +impl ClusterManager for TestClusterManager { + fn contains_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { + true + } + fn add_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError> { + Ok(()) + } + + fn remove_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError> { + Ok(()) + } +} + +pub struct TestClusterCreator; +impl ClusterCreator for TestClusterCreator { + fn create_new_cluster( + _cluster_id: ClusterId, + _cluster_manager_id: T::AccountId, + _cluster_reserve_id: T::AccountId, + _cluster_params: ClusterParams, + _cluster_gov_params: ClusterGovParams, + ) -> DispatchResult { + Ok(()) + } } pub struct ExtBuilder; impl ExtBuilder { - fn build(self) -> TestExternalities { + pub fn build(self) -> TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); diff --git a/pallets/ddc-customers/src/tests.rs b/pallets/ddc-customers/src/tests.rs index 4c9f4f6db..a90d43482 100644 --- a/pallets/ddc-customers/src/tests.rs +++ b/pallets/ddc-customers/src/tests.rs @@ -2,9 +2,7 @@ use super::{mock::*, *}; use ddc_primitives::ClusterId; - use frame_support::{assert_noop, assert_ok}; -use pallet_balances::Error as BalancesError; #[test] fn create_bucket_works() { @@ -76,7 +74,7 @@ fn deposit_and_deposit_extra_works() { // Deposit all tokens fails (should not kill account) assert_noop!( DdcCustomers::deposit(RuntimeOrigin::signed(account_1), 100_u128), - BalancesError::::KeepAlive + Error::::TransferFailed ); let amount1 = 10_u128; diff --git a/pallets/ddc-customers/src/weights.rs b/pallets/ddc-customers/src/weights.rs new file mode 100644 index 000000000..66ae43d01 --- /dev/null +++ b/pallets/ddc-customers/src/weights.rs @@ -0,0 +1,132 @@ + +//! Autogenerated weights for `pallet_ddc_customers` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-16, STEPS: `200`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Raids-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: None, WASM-EXECUTION: Interpreted, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/cere +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_ddc_customers +// --extrinsic +// * +// --steps +// 200 +// --repeat +// 1000 +// --output +// pallets/ddc-customers/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_ddc_nodes. +pub trait WeightInfo { + fn create_bucket() -> Weight; + fn deposit() -> Weight; + fn deposit_extra() -> Weight; + fn unlock_deposit() -> Weight; + fn withdraw_unlocked_deposit_update() -> Weight; + fn withdraw_unlocked_deposit_kill() -> Weight; +} + +/// Weight functions for `pallet_ddc_customers`. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: DdcCustomers BucketsCount (r:1 w:1) + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcCustomers Buckets (r:0 w:1) + fn create_bucket() -> Weight { + Weight::from_ref_time(18_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn deposit() -> Weight { + Weight::from_ref_time(26_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn deposit_extra() -> Weight { + Weight::from_ref_time(28_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + fn unlock_deposit() -> Weight { + Weight::from_ref_time(16_000_000_u64) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + fn withdraw_unlocked_deposit_update() -> Weight { + Weight::from_ref_time(14_000_000_u64) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn withdraw_unlocked_deposit_kill() -> Weight { + Weight::from_ref_time(31_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: DdcCustomers BucketsCount (r:1 w:1) + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: DdcCustomers Buckets (r:0 w:1) + fn create_bucket() -> Weight { + Weight::from_ref_time(18_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn deposit() -> Weight { + Weight::from_ref_time(26_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn deposit_extra() -> Weight { + Weight::from_ref_time(28_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + fn unlock_deposit() -> Weight { + Weight::from_ref_time(16_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + fn withdraw_unlocked_deposit_update() -> Weight { + Weight::from_ref_time(14_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn withdraw_unlocked_deposit_kill() -> Weight { + Weight::from_ref_time(31_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/pallets/ddc-nodes/Cargo.toml b/pallets/ddc-nodes/Cargo.toml index 589bcc7e9..3d189469f 100644 --- a/pallets/ddc-nodes/Cargo.toml +++ b/pallets/ddc-nodes/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } ddc-primitives = { version = "0.1.0", default-features = false, path = "../../primitives" } ddc-traits = { version = "0.1.0", default-features = false, path = "../../traits" } +frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", optional = true } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } @@ -26,6 +27,7 @@ default = ["std"] std = [ "codec/std", "ddc-primitives/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "scale-info/std", @@ -33,3 +35,10 @@ std = [ "sp-std/std", "sp-core/std", ] +runtime-benchmarks = [ + "ddc-primitives/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/pallets/ddc-nodes/src/benchmarking.rs b/pallets/ddc-nodes/src/benchmarking.rs new file mode 100644 index 000000000..448cde563 --- /dev/null +++ b/pallets/ddc-nodes/src/benchmarking.rs @@ -0,0 +1,61 @@ +//! DdcStaking pallet benchmarking. + +use super::*; +use crate::{cdn_node::CDNNodeProps, Pallet as DdcNodes}; +use ddc_primitives::CDNNodePubKey; +use testing_utils::*; + +use sp_std::prelude::*; + +pub use frame_benchmarking::{ + account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, +}; +use frame_system::RawOrigin; + +const USER_SEED: u32 = 999666; + +benchmarks! { + create_node { + let (user, node, cdn_node_params, _) = create_user_and_config::("user", USER_SEED); + + whitelist_account!(user); + }: _(RawOrigin::Signed(user.clone()), node, cdn_node_params) + verify { + assert!(CDNNodes::::contains_key(CDNNodePubKey::new([0; 32]))); + } + + delete_node { + let (user, node, cdn_node_params, _) = create_user_and_config::("user", USER_SEED); + + DdcNodes::::create_node(RawOrigin::Signed(user.clone()).into(),node.clone(), cdn_node_params)?; + + whitelist_account!(user); + }: _(RawOrigin::Signed(user.clone()), node) + verify { + assert!(!CDNNodes::::contains_key(CDNNodePubKey::new([0; 32]))); + } + + set_node_params { + let (user, node, cdn_node_params, cdn_node_params_new) = create_user_and_config::("user", USER_SEED); + + DdcNodes::::create_node(RawOrigin::Signed(user.clone()).into(),node.clone(), cdn_node_params)?; + + whitelist_account!(user); + }: _(RawOrigin::Signed(user.clone()), node, cdn_node_params_new) + verify { + assert_eq!(CDNNodes::::try_get( + CDNNodePubKey::new([0; 32])).unwrap().props, + CDNNodeProps { + host: vec![2u8, 255].try_into().unwrap(), + http_port: 45000u16, + grpc_port: 55000u16, + p2p_port: 65000u16, + }); + } + + impl_benchmark_test_suite!( + DdcNodes, + crate::mock::ExtBuilder.build(), + crate::mock::Test, + ); +} diff --git a/pallets/ddc-nodes/src/cdn_node.rs b/pallets/ddc-nodes/src/cdn_node.rs index eafe6cd27..43c3721f4 100644 --- a/pallets/ddc-nodes/src/cdn_node.rs +++ b/pallets/ddc-nodes/src/cdn_node.rs @@ -1,6 +1,6 @@ -use crate::node::{NodeError, NodeParams, NodeProps, NodeTrait}; +use crate::node::{NodeError, NodeProps, NodeTrait}; use codec::{Decode, Encode}; -use ddc_primitives::{CDNNodePubKey, ClusterId, NodePubKey, NodeType}; +use ddc_primitives::{CDNNodePubKey, ClusterId, NodeParams, NodePubKey, NodeType}; use frame_support::{parameter_types, BoundedVec}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; @@ -28,14 +28,6 @@ pub struct CDNNodeProps { pub p2p_port: u16, } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -pub struct CDNNodeParams { - pub host: Vec, - pub http_port: u16, - pub grpc_port: u16, - pub p2p_port: u16, -} - impl CDNNode { pub fn new( node_pub_key: NodePubKey, diff --git a/pallets/ddc-nodes/src/lib.rs b/pallets/ddc-nodes/src/lib.rs index f18d724b7..f3c7f1f93 100644 --- a/pallets/ddc-nodes/src/lib.rs +++ b/pallets/ddc-nodes/src/lib.rs @@ -19,8 +19,20 @@ pub(crate) mod mock; #[cfg(test)] mod tests; -use ddc_primitives::{CDNNodePubKey, ClusterId, NodePubKey, StorageNodePubKey}; -use ddc_traits::node::{NodeVisitor, NodeVisitorError}; +pub mod weights; +use crate::weights::WeightInfo; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; +#[cfg(any(feature = "runtime-benchmarks", test))] +pub mod testing_utils; + +use ddc_primitives::{CDNNodePubKey, ClusterId, NodeParams, NodePubKey, StorageNodePubKey}; +use ddc_traits::{ + node::{NodeCreator, NodeVisitor, NodeVisitorError}, + staking::StakingVisitor, +}; + use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_std::prelude::*; @@ -31,8 +43,8 @@ mod node; mod storage_node; pub use crate::{ - cdn_node::{CDNNode, CDNNodeParams}, - node::{Node, NodeError, NodeParams, NodeTrait}, + cdn_node::CDNNode, + node::{Node, NodeError, NodeTrait}, storage_node::StorageNode, }; @@ -48,6 +60,8 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type StakingVisitor: StakingVisitor; + type WeightInfo: WeightInfo; } #[pallet::event] @@ -68,6 +82,7 @@ pub mod pallet { OnlyNodeProvider, NodeIsAssignedToCluster, HostLenExceedsLimit, + NodeHasDanglingStake, } #[pallet::storage] @@ -81,7 +96,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::create_node())] pub fn create_node( origin: OriginFor, node_pub_key: NodePubKey, @@ -95,18 +110,20 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::delete_node())] pub fn delete_node(origin: OriginFor, node_pub_key: NodePubKey) -> DispatchResult { let caller_id = ensure_signed(origin)?; let node = Self::get(node_pub_key.clone()).map_err(Into::>::into)?; ensure!(node.get_provider_id() == &caller_id, Error::::OnlyNodeProvider); ensure!(node.get_cluster_id().is_none(), Error::::NodeIsAssignedToCluster); + let has_stake = T::StakingVisitor::has_stake(&node_pub_key); + ensure!(!has_stake, Error::::NodeHasDanglingStake); Self::delete(node_pub_key.clone()).map_err(Into::>::into)?; Self::deposit_event(Event::::NodeDeleted { node_pub_key }); Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_node_params())] pub fn set_node_params( origin: OriginFor, node_pub_key: NodePubKey, @@ -129,6 +146,7 @@ pub mod pallet { fn delete(node_pub_key: NodePubKey) -> Result<(), NodeRepositoryError>; } + #[derive(Debug, PartialEq)] pub enum NodeRepositoryError { StorageNodeAlreadyExists, CDNNodeAlreadyExists, @@ -220,5 +238,22 @@ pub mod pallet { Self::get(node_pub_key.clone()).map_err(|_| NodeVisitorError::NodeDoesNotExist)?; Ok(*node.get_cluster_id()) } + + fn exists(node_pub_key: &NodePubKey) -> bool { + Self::get(node_pub_key.clone()).is_ok() + } + } + + impl NodeCreator for Pallet { + fn create_node( + node_pub_key: NodePubKey, + provider_id: T::AccountId, + node_params: NodeParams, + ) -> DispatchResult { + let node = Node::::new(node_pub_key, provider_id, node_params) + .map_err(Into::>::into)?; + Self::create(node).map_err(Into::>::into)?; + Ok(()) + } } } diff --git a/pallets/ddc-nodes/src/mock.rs b/pallets/ddc-nodes/src/mock.rs index 18377ac58..ca74d8af7 100644 --- a/pallets/ddc-nodes/src/mock.rs +++ b/pallets/ddc-nodes/src/mock.rs @@ -3,6 +3,7 @@ #![allow(dead_code)] use crate::{self as pallet_ddc_nodes, *}; +use ddc_traits::staking::{StakingVisitor, StakingVisitorError}; use frame_support::{ construct_runtime, parameter_types, traits::{ConstU32, ConstU64, Everything}, @@ -90,6 +91,24 @@ impl pallet_timestamp::Config for Test { impl crate::pallet::Config for Test { type RuntimeEvent = RuntimeEvent; + type StakingVisitor = TestStakingVisitor; + type WeightInfo = (); +} + +pub struct TestStakingVisitor; +impl StakingVisitor for TestStakingVisitor { + fn has_activated_stake( + _node_pub_key: &NodePubKey, + _cluster_id: &ClusterId, + ) -> Result { + Ok(false) + } + fn has_stake(_node_pub_key: &NodePubKey) -> bool { + false + } + fn has_chilling_attempt(_node_pub_key: &NodePubKey) -> Result { + Ok(false) + } } pub(crate) type TestRuntimeCall = ::RuntimeCall; @@ -97,7 +116,7 @@ pub(crate) type TestRuntimeCall = ::RuntimeCall; pub struct ExtBuilder; impl ExtBuilder { - fn build(self) -> TestExternalities { + pub fn build(self) -> TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); diff --git a/pallets/ddc-nodes/src/node.rs b/pallets/ddc-nodes/src/node.rs index 042d13be3..f0a5be5d5 100644 --- a/pallets/ddc-nodes/src/node.rs +++ b/pallets/ddc-nodes/src/node.rs @@ -1,13 +1,13 @@ #![allow(clippy::needless_lifetimes)] // ToDo use crate::{ - cdn_node::{CDNNode, CDNNodeParams, CDNNodeProps}, + cdn_node::{CDNNode, CDNNodeProps}, pallet::Error, - storage_node::{StorageNode, StorageNodeParams, StorageNodeProps}, + storage_node::{StorageNode, StorageNodeProps}, ClusterId, }; use codec::{Decode, Encode}; -use ddc_primitives::{NodePubKey, NodeType}; +use ddc_primitives::{NodeParams, NodePubKey, NodeType}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; @@ -17,13 +17,6 @@ pub enum Node { CDN(CDNNode), } -// Params fields are always coming from extrinsic input -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -pub enum NodeParams { - StorageParams(StorageNodeParams), - CDNParams(CDNNodeParams), -} - // Props fields may include internal protocol properties #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub enum NodeProps { @@ -108,6 +101,7 @@ impl NodeTrait for Node { } } +#[derive(Debug, PartialEq)] pub enum NodeError { InvalidStorageNodePubKey, InvalidCDNNodePubKey, diff --git a/pallets/ddc-nodes/src/storage_node.rs b/pallets/ddc-nodes/src/storage_node.rs index 902739771..0749c044e 100644 --- a/pallets/ddc-nodes/src/storage_node.rs +++ b/pallets/ddc-nodes/src/storage_node.rs @@ -1,10 +1,9 @@ -use crate::node::{NodeError, NodeParams, NodeProps, NodeTrait}; +use crate::node::{NodeError, NodeProps, NodeTrait}; use codec::{Decode, Encode}; -use ddc_primitives::{ClusterId, NodePubKey, NodeType, StorageNodePubKey}; +use ddc_primitives::{ClusterId, NodeParams, NodePubKey, NodeType, StorageNodePubKey}; use frame_support::{parameter_types, BoundedVec}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; -use sp_std::prelude::Vec; parameter_types! { pub MaxStorageNodeParamsLen: u16 = 2048; @@ -28,14 +27,6 @@ pub struct StorageNodeProps { pub p2p_port: u16, } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -pub struct StorageNodeParams { - pub host: Vec, - pub http_port: u16, - pub grpc_port: u16, - pub p2p_port: u16, -} - impl StorageNode { pub fn new( node_pub_key: NodePubKey, diff --git a/pallets/ddc-nodes/src/testing_utils.rs b/pallets/ddc-nodes/src/testing_utils.rs new file mode 100644 index 000000000..8db4dfd35 --- /dev/null +++ b/pallets/ddc-nodes/src/testing_utils.rs @@ -0,0 +1,31 @@ +//! Testing utils for ddc-staking. + +use crate::{Config, NodePubKey}; +use ddc_primitives::{CDNNodeParams, CDNNodePubKey, NodeParams}; +use frame_benchmarking::account; +use sp_std::vec; + +const SEED: u32 = 0; + +/// Grab a funded user. +pub fn create_user_and_config( + string: &'static str, + n: u32, +) -> (T::AccountId, NodePubKey, NodeParams, NodeParams) { + let user = account(string, n, SEED); + let node = NodePubKey::CDNPubKey(CDNNodePubKey::new([0; 32])); + let cdn_node_params = NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }); + + let cdn_node_params_new = NodeParams::CDNParams(CDNNodeParams { + host: vec![2u8, 255], + http_port: 45000u16, + grpc_port: 55000u16, + p2p_port: 65000u16, + }); + (user, node, cdn_node_params, cdn_node_params_new) +} diff --git a/pallets/ddc-nodes/src/tests.rs b/pallets/ddc-nodes/src/tests.rs index 1c5a80137..ea2c6d8af 100644 --- a/pallets/ddc-nodes/src/tests.rs +++ b/pallets/ddc-nodes/src/tests.rs @@ -1,13 +1,12 @@ //! Tests for the module. use super::{mock::*, *}; -use crate::{cdn_node::CDNNodeParams, storage_node::StorageNodeParams}; -use ddc_primitives::NodePubKey; +use ddc_primitives::{CDNNodeParams, NodePubKey, StorageNodeParams}; use frame_support::{assert_noop, assert_ok}; use sp_runtime::AccountId32; #[test] -fn create_node_works() { +fn create_cdn_node_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); let bytes = [0u8; 32]; @@ -34,6 +33,31 @@ fn create_node_works() { Error::::InvalidNodeParams ); + // Pub key invalid + assert_noop!( + CDNNode::::new( + NodePubKey::StoragePubKey(node_pub_key.clone()), + 1u64, + NodeParams::CDNParams(cdn_node_params.clone()) + ), + NodeError::InvalidCDNNodePubKey + ); + + // Host length exceeds limit + assert_noop!( + DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::CDNPubKey(node_pub_key.clone()), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8; 256], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + ), + Error::::HostLenExceedsLimit + ); + // Node created assert_ok!(DdcNodes::create_node( RuntimeOrigin::signed(1), @@ -41,6 +65,17 @@ fn create_node_works() { NodeParams::CDNParams(cdn_node_params.clone()) )); + // Check storage + assert!(CDNNodes::::contains_key(node_pub_key.clone())); + assert!(DdcNodes::exists(&NodePubKey::CDNPubKey(node_pub_key.clone()))); + if let Ok(cluster_id) = + DdcNodes::get_cluster_id(&NodePubKey::CDNPubKey(node_pub_key.clone())) + { + assert_eq!(cluster_id, None); + } + let cdn_node = DdcNodes::cdn_nodes(&node_pub_key).unwrap(); + assert_eq!(cdn_node.pub_key, node_pub_key); + // Node already exists assert_noop!( DdcNodes::create_node( @@ -60,7 +95,162 @@ fn create_node_works() { } #[test] -fn set_node_params_works() { +fn create_cdn_node_with_node_creator() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + let bytes = [0u8; 32]; + let node_pub_key = AccountId32::from(bytes); + let cdn_node_params = CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + + // Node created + assert_ok!(>::create_node( + NodePubKey::CDNPubKey(node_pub_key.clone()), + 1u64, + NodeParams::CDNParams(cdn_node_params) + )); + + // Check storage + assert!(CDNNodes::::contains_key(node_pub_key.clone())); + assert!(DdcNodes::exists(&NodePubKey::CDNPubKey(node_pub_key.clone()))); + if let Ok(cluster_id) = + DdcNodes::get_cluster_id(&NodePubKey::CDNPubKey(node_pub_key.clone())) + { + assert_eq!(cluster_id, None); + } + let cdn_node = DdcNodes::cdn_nodes(&node_pub_key).unwrap(); + assert_eq!(cdn_node.pub_key, node_pub_key); + }) +} + +#[test] +fn create_storage_node_works() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + let bytes = [0u8; 32]; + let node_pub_key = AccountId32::from(bytes); + let storage_node_params = StorageNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + + // Node params are not valid + assert_noop!( + DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + ), + Error::::InvalidNodeParams + ); + + // Pub key invalid + assert_noop!( + StorageNode::::new( + NodePubKey::CDNPubKey(node_pub_key.clone()), + 1u64, + NodeParams::StorageParams(storage_node_params.clone()) + ), + NodeError::InvalidStorageNodePubKey + ); + + // Host length exceeds limit + assert_noop!( + DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(StorageNodeParams { + host: vec![1u8; 256], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + ), + Error::::HostLenExceedsLimit + ); + + // Node created + assert_ok!(DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params.clone()) + )); + + // Check storage + assert!(StorageNodes::::contains_key(node_pub_key.clone())); + assert!(DdcNodes::exists(&NodePubKey::StoragePubKey(node_pub_key.clone()))); + if let Ok(cluster_id) = + DdcNodes::get_cluster_id(&NodePubKey::StoragePubKey(node_pub_key.clone())) + { + assert_eq!(cluster_id, None); + } + let storage_node = DdcNodes::storage_nodes(&node_pub_key).unwrap(); + assert_eq!(storage_node.pub_key, node_pub_key); + + // Node already exists + assert_noop!( + DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params) + ), + Error::::NodeAlreadyExists + ); + + // Checking that event was emitted + assert_eq!(System::events().len(), 1); + System::assert_last_event( + Event::NodeCreated { node_pub_key: NodePubKey::StoragePubKey(node_pub_key) }.into(), + ) + }) +} + +#[test] +fn create_storage_node_with_node_creator() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + let bytes = [0u8; 32]; + let node_pub_key = AccountId32::from(bytes); + let storage_node_params = StorageNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + + // Node created + assert_ok!(>::create_node( + NodePubKey::StoragePubKey(node_pub_key.clone()), + 1u64, + NodeParams::StorageParams(storage_node_params) + )); + + // Check storage + assert!(StorageNodes::::contains_key(node_pub_key.clone())); + assert!(DdcNodes::exists(&NodePubKey::StoragePubKey(node_pub_key.clone()))); + if let Ok(cluster_id) = + DdcNodes::get_cluster_id(&NodePubKey::StoragePubKey(node_pub_key.clone())) + { + assert_eq!(cluster_id, None); + } + let cdn_node = DdcNodes::storage_nodes(&node_pub_key).unwrap(); + assert_eq!(cdn_node.pub_key, node_pub_key); + }) +} + +#[test] +fn set_cdn_node_params_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); let bytes = [0u8; 32]; @@ -117,11 +307,55 @@ fn set_node_params_works() { DdcNodes::set_node_params( RuntimeOrigin::signed(2), NodePubKey::CDNPubKey(node_pub_key.clone()), - NodeParams::CDNParams(cdn_node_params) + NodeParams::CDNParams(cdn_node_params.clone()) ), Error::::OnlyNodeProvider ); + // CDN host length exceeds limit + assert_noop!( + DdcNodes::set_node_params( + RuntimeOrigin::signed(1), + NodePubKey::CDNPubKey(node_pub_key.clone()), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8; 256], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + ), + Error::::HostLenExceedsLimit + ); + + let bytes_2 = [1u8; 32]; + let node_pub_key_2 = AccountId32::from(bytes_2); + let node = Node::::new( + NodePubKey::CDNPubKey(node_pub_key_2), + 2u64, + NodeParams::CDNParams(cdn_node_params), + ) + .unwrap(); + + // Update should fail if node doesn't exist + assert_noop!( + >::update(node), + NodeRepositoryError::CDNNodeDoesNotExist + ); + + assert_noop!( + DdcNodes::set_node_params( + RuntimeOrigin::signed(1), + NodePubKey::CDNPubKey(node_pub_key.clone()), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8; 256], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + ), + Error::::HostLenExceedsLimit + ); + // Checking that event was emitted assert_eq!(System::events().len(), 2); System::assert_last_event( @@ -131,7 +365,109 @@ fn set_node_params_works() { } #[test] -fn set_delete_node_works() { +fn set_storage_node_params_works() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + let bytes = [0u8; 32]; + let node_pub_key = AccountId32::from(bytes); + let storage_node_params = StorageNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + let cdn_node_params = CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + + // Node doesn't exist + assert_noop!( + DdcNodes::set_node_params( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params.clone()) + ), + Error::::NodeDoesNotExist + ); + + // Node created + assert_ok!(DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params.clone()) + )); + + // Set node params + assert_ok!(DdcNodes::set_node_params( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params.clone()) + )); + + // Node params are not valid + assert_noop!( + DdcNodes::set_node_params( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::CDNParams(cdn_node_params) + ), + Error::::InvalidNodeParams + ); + + // Only node provider can set params + assert_noop!( + DdcNodes::set_node_params( + RuntimeOrigin::signed(2), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params.clone()) + ), + Error::::OnlyNodeProvider + ); + + let bytes_2 = [1u8; 32]; + let node_pub_key_2 = AccountId32::from(bytes_2); + let node = Node::::new( + NodePubKey::StoragePubKey(node_pub_key_2), + 2u64, + NodeParams::StorageParams(storage_node_params), + ) + .unwrap(); + + // Update should fail if node doesn't exist + assert_noop!( + >::update(node), + NodeRepositoryError::StorageNodeDoesNotExist + ); + + // Storage host length exceeds limit + assert_noop!( + DdcNodes::set_node_params( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(StorageNodeParams { + host: vec![1u8; 256], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + ), + Error::::HostLenExceedsLimit + ); + + // Checking that event was emitted + assert_eq!(System::events().len(), 2); + System::assert_last_event( + Event::NodeParamsChanged { node_pub_key: NodePubKey::StoragePubKey(node_pub_key) } + .into(), + ) + }) +} + +#[test] +fn delete_cdn_node_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); let bytes = [0u8; 32]; @@ -181,3 +517,55 @@ fn set_delete_node_works() { ) }) } + +#[test] +fn delete_storage_node_works() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + let bytes = [0u8; 32]; + let node_pub_key = AccountId32::from(bytes); + let storage_node_params = StorageNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }; + + // Node doesn't exist + assert_noop!( + DdcNodes::delete_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()) + ), + Error::::NodeDoesNotExist + ); + + // Create node + assert_ok!(DdcNodes::create_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + NodeParams::StorageParams(storage_node_params) + )); + + // Only node provider can delete + assert_noop!( + DdcNodes::delete_node( + RuntimeOrigin::signed(2), + NodePubKey::StoragePubKey(node_pub_key.clone()) + ), + Error::::OnlyNodeProvider + ); + + // Delete node + assert_ok!(DdcNodes::delete_node( + RuntimeOrigin::signed(1), + NodePubKey::StoragePubKey(node_pub_key.clone()), + )); + + // Checking that event was emitted + assert_eq!(System::events().len(), 2); + System::assert_last_event( + Event::NodeDeleted { node_pub_key: NodePubKey::StoragePubKey(node_pub_key) }.into(), + ) + }) +} diff --git a/pallets/ddc-nodes/src/weights.rs b/pallets/ddc-nodes/src/weights.rs new file mode 100644 index 000000000..868380f40 --- /dev/null +++ b/pallets/ddc-nodes/src/weights.rs @@ -0,0 +1,83 @@ + +//! Autogenerated weights for `pallet_ddc_nodes` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-16, STEPS: `200`, REPEAT: 1000, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Raids-MBP-2`, CPU: `` +//! EXECUTION: None, WASM-EXECUTION: Interpreted, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/cere +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_ddc_nodes +// --extrinsic +// * +// --steps +// 200 +// --repeat +// 1000 +// --output +// pallets/ddc-nodes/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_ddc_nodes. +pub trait WeightInfo { + fn create_node() -> Weight; + fn delete_node() -> Weight; + fn set_node_params() -> Weight; +} + +/// Weights for pallet_ddc_nodes. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: DdcNodes CDNNodes (r:1 w:1) + fn create_node() -> Weight { + Weight::from_ref_time(12_000_000u64) + .saturating_add(T::DbWeight::get().reads(1u64)) + .saturating_add(T::DbWeight::get().writes(1u64)) + } + // Storage: DdcNodes CDNNodes (r:1 w:1) + fn delete_node() -> Weight { + Weight::from_ref_time(14_000_000u64) + .saturating_add(T::DbWeight::get().reads(1u64)) + .saturating_add(T::DbWeight::get().writes(1u64)) + } + // Storage: DdcNodes CDNNodes (r:1 w:1) + fn set_node_params() -> Weight { + Weight::from_ref_time(15_000_000u64) + .saturating_add(T::DbWeight::get().reads(1u64)) + .saturating_add(T::DbWeight::get().writes(1u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: DdcNodes CDNNodes (r:1 w:1) + fn create_node() -> Weight { + Weight::from_ref_time(12_000_000u64) + .saturating_add(RocksDbWeight::get().reads(1u64)) + .saturating_add(RocksDbWeight::get().writes(1u64)) + } + // Storage: DdcNodes CDNNodes (r:1 w:1) + fn delete_node() -> Weight { + Weight::from_ref_time(14_000_000u64) + .saturating_add(RocksDbWeight::get().reads(1u64)) + .saturating_add(RocksDbWeight::get().writes(1u64)) + } + // Storage: DdcNodes CDNNodes (r:1 w:1) + fn set_node_params() -> Weight { + Weight::from_ref_time(15_000_000u64) + .saturating_add(RocksDbWeight::get().reads(1u64)) + .saturating_add(RocksDbWeight::get().writes(1u64)) + } +} \ No newline at end of file diff --git a/pallets/ddc-payouts/Cargo.toml b/pallets/ddc-payouts/Cargo.toml index 534342060..82b5f34c7 100644 --- a/pallets/ddc-payouts/Cargo.toml +++ b/pallets/ddc-payouts/Cargo.toml @@ -42,4 +42,11 @@ std = [ "sp-core/std", "frame-election-provider-support/std", ] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] + +runtime-benchmarks = [ + "ddc-primitives/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/pallets/ddc-payouts/src/benchmarking.rs b/pallets/ddc-payouts/src/benchmarking.rs new file mode 100644 index 000000000..80c6528cb --- /dev/null +++ b/pallets/ddc-payouts/src/benchmarking.rs @@ -0,0 +1,500 @@ +//! DdcPayouts pallet benchmarking. + +use ddc_primitives::{ClusterGovParams, ClusterId, ClusterParams}; +pub use frame_benchmarking::{account, benchmarks, whitelist_account}; +use frame_system::RawOrigin; +use sp_runtime::Perquintill; +use sp_std::prelude::*; + +use super::*; +use crate::Pallet as DdcPayouts; + +const CERE: u128 = 10000000000; + +fn create_dac_account() -> T::AccountId { + let dac_account = create_account::("dac_account", 0, 0); + authorize_account::(dac_account.clone()); + dac_account +} + +fn create_account(name: &'static str, idx: u32, seed: u32) -> T::AccountId { + account::(name, idx, seed) +} + +fn authorize_account(account: T::AccountId) { + AuthorisedCaller::::put(account); +} + +fn endow_account(account: &T::AccountId, amount: u128) { + let balance = amount.saturated_into::>(); + let _ = T::Currency::make_free_balance_be(account, balance); +} + +fn endow_customer(customer: &T::AccountId, amount: u128) { + endow_account::(customer, amount); + T::CustomerDepositor::deposit( + customer.clone(), + // we need to keep min existensial deposit + amount - T::Currency::minimum_balance().saturated_into::(), + ) + .expect("Customer deposit failed"); +} + +fn create_cluster( + cluster_id: ClusterId, + cluster_manager_id: T::AccountId, + cluster_reserve_id: T::AccountId, + cluster_params: ClusterParams, + cluster_gov_params: ClusterGovParams, T::BlockNumber>, +) { + T::ClusterCreator::create_new_cluster( + cluster_id, + cluster_manager_id, + cluster_reserve_id, + cluster_params, + cluster_gov_params, + ) + .expect("Cluster is not created"); +} + +fn create_default_cluster(cluster_id: ClusterId) { + let cluster_manager = create_account::("cm", 0, 0); + let cluster_reserve = create_account::("cr", 0, 0); + let cluster_params = ClusterParams { node_provider_auth_contract: Default::default() }; + let cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::from_percent(5), + validators_share: Perquintill::from_percent(10), + cluster_reserve_share: Perquintill::from_percent(15), + unit_per_mb_stored: CERE, + unit_per_mb_streamed: CERE, + unit_per_put_request: CERE, + unit_per_get_request: CERE, + ..Default::default() + }; + + create_cluster::( + cluster_id, + cluster_manager, + cluster_reserve, + cluster_params, + cluster_gov_params, + ); +} + +struct BillingReportParams { + cluster_id: ClusterId, + era: DdcEra, + state: State, + total_customer_charge: CustomerCharge, + total_distributed_reward: u128, + total_node_usage: NodeUsage, + charging_max_batch_index: BatchIndex, + charging_processed_batches: BoundedBTreeSet, + rewarding_max_batch_index: BatchIndex, + rewarding_processed_batches: BoundedBTreeSet, +} + +fn create_billing_report(params: BillingReportParams) { + let vault = DdcPayouts::::sub_account_id(params.cluster_id, params.era); + let billing_report = BillingReport:: { + vault, + state: params.state, + total_customer_charge: params.total_customer_charge, + total_distributed_reward: params.total_distributed_reward, + total_node_usage: params.total_node_usage, + charging_max_batch_index: params.charging_max_batch_index, + charging_processed_batches: params.charging_processed_batches, + rewarding_max_batch_index: params.rewarding_max_batch_index, + rewarding_processed_batches: params.rewarding_processed_batches, + }; + + ActiveBillingReports::::insert(params.cluster_id, params.era, billing_report); +} + +benchmarks! { + + set_authorised_caller { + let dac_account = create_account::("dac_account", 0, 0); + + }: _(RawOrigin::Root, dac_account.clone()) + verify { + assert_eq!(AuthorisedCaller::::get(), Some(dac_account)); + } + + begin_billing_report { + + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + + create_default_cluster::(cluster_id); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era) + verify { + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::Initialized); + } + + begin_charging_customers { + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::Initialized; + let total_customer_charge = CustomerCharge::default(); + let total_distributed_reward : u128= 0; + let total_node_usage = NodeUsage::default(); + let charging_max_batch_index = BatchIndex::default(); + let charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + let rewarding_max_batch_index = BatchIndex::default(); + let rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge, + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + let max_batch_index: BatchIndex = 10; + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, max_batch_index) + verify { + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::ChargingCustomers); + assert_eq!(billing_report.charging_max_batch_index, max_batch_index); + } + + send_charging_customers_batch { + let b in 1 .. MaxBatchSize::get() as u32; + + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::ChargingCustomers; + let total_customer_charge = CustomerCharge::default(); + let total_distributed_reward : u128 = 0; + let total_node_usage = NodeUsage::default(); + let charging_max_batch_index = 0; + let charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + let rewarding_max_batch_index = BatchIndex::default(); + let rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge, + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let batch_index: BatchIndex = 0; + let payers: Vec<(T::AccountId, CustomerUsage)> = (0..b).map(|i| { + let customer = create_account::("customer", i, i); + + if b % 2 == 0 { + // no customer debt path + endow_customer::(&customer, 1_000_000 * CERE); + } else { + // customer debt path + endow_customer::(&customer, 10 * CERE); + } + + let customer_usage = CustomerUsage { + transferred_bytes: 200000000, // 200 mb + stored_bytes: 100000000, // 100 mb + number_of_gets: 10, // 10 gets + number_of_puts: 5, // 5 puts + }; + + (customer, customer_usage) + }).collect(); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, batch_index, payers) + verify { + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::ChargingCustomers); + assert!(billing_report.charging_processed_batches.contains(&batch_index)); + } + + end_charging_customers { + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::ChargingCustomers; + let total_customer_charge = CustomerCharge { + transfer: 200 * CERE, // price for 200 mb + storage: 100 * CERE, // price for 100 mb + gets: 10 * CERE, // price for 10 gets + puts: 5 * CERE, // price for 5 puts + }; + let total_distributed_reward : u128 = 0; + let total_node_usage = NodeUsage::default(); + let charging_max_batch_index = 0; + let mut charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + charging_processed_batches.try_insert(0).unwrap(); + let rewarding_max_batch_index = BatchIndex::default(); + let rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge: total_customer_charge.clone(), + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let vault = DdcPayouts::::sub_account_id(cluster_id, era); + let total_customer_charge_amount = total_customer_charge.transfer + total_customer_charge.storage + total_customer_charge.gets + total_customer_charge.puts; + endow_account::(&vault, total_customer_charge_amount); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era) + verify { + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::CustomersChargedWithFees); + assert!(billing_report.charging_processed_batches.contains(&charging_max_batch_index)); + } + + begin_rewarding_providers { + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::CustomersChargedWithFees; + let total_customer_charge = CustomerCharge { + transfer: 200 * CERE, // price for 200 mb + storage: 100 * CERE, // price for 100 mb + gets: 10 * CERE, // price for 10 gets + puts: 5 * CERE, // price for 5 puts + }; + let total_distributed_reward : u128 = 0; + let total_node_usage = NodeUsage::default(); + let charging_max_batch_index = 0; + let mut charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + charging_processed_batches.try_insert(0).unwrap(); + let rewarding_max_batch_index = BatchIndex::default(); + let rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge, + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let max_batch_index: BatchIndex = 10; + let total_node_usage = NodeUsage { + transferred_bytes: 200000000, // 200 mb + stored_bytes: 100000000, // 100 mb + number_of_gets: 10, // 10 gets + number_of_puts: 5, // 5 puts + }; + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, max_batch_index, total_node_usage) + verify { + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::RewardingProviders); + assert_eq!(billing_report.rewarding_max_batch_index, max_batch_index); + } + + send_rewarding_providers_batch { + let b in 1 .. MaxBatchSize::get() as u32; + + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::RewardingProviders; + let total_customer_charge = CustomerCharge { + transfer: (200 * CERE).saturating_mul(b.into()), // price for 200 mb per customer + storage: (100 * CERE).saturating_mul(b.into()), // price for 100 mb per customer + gets: (10 * CERE).saturating_mul(b.into()), // price for 10 gets per customer + puts: (5 * CERE).saturating_mul(b.into()), // price for 5 puts per customer + }; + let total_distributed_reward : u128 = 0; + let total_node_usage = NodeUsage { + transferred_bytes: 200000000u64.saturating_mul(b.into()), // 200 mb per provider + stored_bytes: 100000000u64.saturating_mul(b.into()), // 100 mb per provider + number_of_gets: 10u128.saturating_mul(b.into()), // 10 gets per provider + number_of_puts: 5u128.saturating_mul(b.into()), // 5 puts per provider + }; + let charging_max_batch_index = 0; + let mut charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + charging_processed_batches.try_insert(0).unwrap(); + let rewarding_max_batch_index = 0; + let rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge: total_customer_charge.clone(), + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let vault = DdcPayouts::::sub_account_id(cluster_id, era); + let total_customer_charge_amount = total_customer_charge.transfer + total_customer_charge.storage + total_customer_charge.gets + total_customer_charge.puts; + endow_account::(&vault, total_customer_charge_amount + T::Currency::minimum_balance().saturated_into::()); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + let batch_index: BatchIndex = 0; + let payees: Vec<(T::AccountId, NodeUsage)> = (0..b).map(|i| { + let provider = create_account::("provider", i, i); + endow_account::(&provider, T::Currency::minimum_balance().saturated_into()); + let node_usage = NodeUsage { + transferred_bytes: 200000000, // 200 mb + stored_bytes: 100000000, // 100 mb + number_of_gets: 10, // 10 gets + number_of_puts: 5, // 5 puts + }; + (provider, node_usage) + }).collect(); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era, batch_index, payees) + verify { + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::RewardingProviders); + assert!(billing_report.rewarding_processed_batches.contains(&batch_index)); + } + + end_rewarding_providers { + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::RewardingProviders; + let total_customer_charge = CustomerCharge { + transfer: 200 * CERE, // price for 200 mb + storage: 100 * CERE, // price for 100 mb + gets: 10 * CERE, // price for 10 gets + puts: 5 * CERE, // price for 5 puts + }; + let total_distributed_reward : u128 = total_customer_charge.transfer + total_customer_charge.storage + total_customer_charge.gets + total_customer_charge.puts; + let total_node_usage = NodeUsage { + transferred_bytes: 200000000, // 200 mb + stored_bytes: 100000000, // 100 mb + number_of_gets: 10, // 10 gets + number_of_puts: 5, // 5 puts + }; + let charging_max_batch_index = 0; + let mut charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + charging_processed_batches.try_insert(0).unwrap(); + let rewarding_max_batch_index = 0; + let mut rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + rewarding_processed_batches.try_insert(0).unwrap(); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge, + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era) + verify { + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::ProvidersRewarded); + } + + end_billing_report { + let cluster_id = ClusterId::from([1; 20]); + let era : DdcEra = 1; + let state = State::ProvidersRewarded; + let total_customer_charge = CustomerCharge { + transfer: 200 * CERE, // price for 200 mb + storage: 100 * CERE, // price for 100 mb + gets: 10 * CERE, // price for 10 gets + puts: 5 * CERE, // price for 5 puts + }; + let total_distributed_reward : u128 = total_customer_charge.transfer + total_customer_charge.storage + total_customer_charge.gets + total_customer_charge.puts; + let total_node_usage = NodeUsage { + transferred_bytes: 200000000, // 200 mb + stored_bytes: 100000000, // 100 mb + number_of_gets: 10, // 10 gets + number_of_puts: 5, // 5 puts + }; + let charging_max_batch_index = 0; + let mut charging_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + charging_processed_batches.try_insert(0).unwrap(); + let rewarding_max_batch_index = 0; + let mut rewarding_processed_batches : BoundedBTreeSet = BoundedBTreeSet::default(); + rewarding_processed_batches.try_insert(0).unwrap(); + + create_default_cluster::(cluster_id); + create_billing_report::(BillingReportParams { + cluster_id, + era, + state, + total_customer_charge, + total_distributed_reward, + total_node_usage, + charging_max_batch_index, + charging_processed_batches, + rewarding_max_batch_index, + rewarding_processed_batches, + }); + + let dac_account = create_dac_account::(); + whitelist_account!(dac_account); + + }: _(RawOrigin::Signed(dac_account.clone()), cluster_id, era) + verify { + assert!(ActiveBillingReports::::contains_key(cluster_id, era)); + let billing_report = ActiveBillingReports::::get(cluster_id, era).unwrap(); + assert_eq!(billing_report.state, State::Finalized); + } + +} diff --git a/pallets/ddc-payouts/src/lib.rs b/pallets/ddc-payouts/src/lib.rs index d17052f6c..d61d22059 100644 --- a/pallets/ddc-payouts/src/lib.rs +++ b/pallets/ddc-payouts/src/lib.rs @@ -14,6 +14,12 @@ #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] +pub mod weights; +use crate::weights::WeightInfo; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + #[cfg(test)] pub(crate) mod mock; #[cfg(test)] @@ -21,8 +27,11 @@ mod tests; use ddc_primitives::{ClusterId, DdcEra}; use ddc_traits::{ - cluster::ClusterVisitor as ClusterVisitorType, - customer::CustomerCharger as CustomerChargerType, pallet::PalletVisitor as PalletVisitorType, + cluster::{ClusterCreator as ClusterCreatorType, ClusterVisitor as ClusterVisitorType}, + customer::{ + CustomerCharger as CustomerChargerType, CustomerDepositor as CustomerDepositorType, + }, + pallet::PalletVisitor as PalletVisitorType, }; use frame_election_provider_support::SortedListProvider; use frame_support::{ @@ -90,6 +99,7 @@ pub type BalanceOf = parameter_types! { pub MaxBatchesCount: u16 = 1000; pub MaxDust: u16 = 20000; + pub MaxBatchSize: u16 = 1000; } #[frame_support::pallet] @@ -111,9 +121,12 @@ pub mod pallet { type PalletId: Get; type Currency: LockableCurrency; type CustomerCharger: CustomerChargerType; + type CustomerDepositor: CustomerDepositorType; type TreasuryVisitor: PalletVisitorType; type ClusterVisitor: ClusterVisitorType; type ValidatorList: SortedListProvider; + type ClusterCreator: ClusterCreatorType>; + type WeightInfo: WeightInfo; } #[pallet::event] @@ -204,6 +217,7 @@ pub mod pallet { BoundedVecOverflow, ArithmeticOverflow, NotExpectedClusterState, + BatchSizeIsOutOfBounds, } #[pallet::storage] @@ -274,7 +288,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::set_authorised_caller())] pub fn set_authorised_caller( origin: OriginFor, authorised_caller: T::AccountId, @@ -288,7 +302,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::begin_billing_report())] pub fn begin_billing_report( origin: OriginFor, cluster_id: ClusterId, @@ -316,7 +330,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::begin_charging_customers())] pub fn begin_charging_customers( origin: OriginFor, cluster_id: ClusterId, @@ -342,7 +356,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::send_charging_customers_batch(payers.len().saturated_into()))] pub fn send_charging_customers_batch( origin: OriginFor, cluster_id: ClusterId, @@ -353,6 +367,11 @@ pub mod pallet { let caller = ensure_signed(origin)?; ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!( + !payers.is_empty() && payers.len() <= MaxBatchSize::get() as usize, + Error::::BatchSizeIsOutOfBounds + ); + let billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; @@ -483,7 +502,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::end_charging_customers())] pub fn end_charging_customers( origin: OriginFor, cluster_id: ClusterId, @@ -581,7 +600,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::begin_rewarding_providers())] pub fn begin_rewarding_providers( origin: OriginFor, cluster_id: ClusterId, @@ -612,7 +631,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::send_rewarding_providers_batch(payees.len().saturated_into()))] pub fn send_rewarding_providers_batch( origin: OriginFor, cluster_id: ClusterId, @@ -623,6 +642,11 @@ pub mod pallet { let caller = ensure_signed(origin)?; ensure!(Self::authorised_caller() == Some(caller), Error::::Unauthorised); + ensure!( + !payees.is_empty() && payees.len() <= MaxBatchSize::get() as usize, + Error::::BatchSizeIsOutOfBounds + ); + let billing_report = ActiveBillingReports::::try_get(cluster_id, era) .map_err(|_| Error::::BillingReportDoesNotExist)?; @@ -691,7 +715,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::end_rewarding_providers())] pub fn end_rewarding_providers( origin: OriginFor, cluster_id: ClusterId, @@ -737,7 +761,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(10_000)] + #[pallet::weight(T::WeightInfo::end_billing_report())] pub fn end_billing_report( origin: OriginFor, cluster_id: ClusterId, diff --git a/pallets/ddc-payouts/src/mock.rs b/pallets/ddc-payouts/src/mock.rs index 74abd95f2..b0f184a9b 100644 --- a/pallets/ddc-payouts/src/mock.rs +++ b/pallets/ddc-payouts/src/mock.rs @@ -3,10 +3,13 @@ #![allow(dead_code)] use crate::{self as pallet_ddc_payouts, *}; -use ddc_primitives::{ClusterFeesParams, ClusterPricingParams, NodePubKey, NodeType}; +use ddc_primitives::{ + ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterParams, ClusterPricingParams, + NodeType, +}; use ddc_traits::{ - cluster::{ClusterVisitor, ClusterVisitorError}, - customer::CustomerCharger, + cluster::{ClusterCreator, ClusterVisitor, ClusterVisitorError}, + customer::{CustomerCharger, CustomerDepositor}, pallet::PalletVisitor, }; use frame_election_provider_support::SortedListProvider; @@ -102,9 +105,12 @@ impl crate::pallet::Config for Test { type PalletId = PayoutsPalletId; type Currency = Balances; type CustomerCharger = TestCustomerCharger; + type CustomerDepositor = TestCustomerDepositor; type ClusterVisitor = TestClusterVisitor; type TreasuryVisitor = TestTreasuryVisitor; type ValidatorList = TestValidatorVisitor; + type ClusterCreator = TestClusterCreator; + type WeightInfo = (); } pub struct TestCustomerCharger; @@ -137,6 +143,29 @@ impl CustomerCharger for TestCustomerCharger { } pub const ACCOUNT_ID_5: AccountId = 5; +pub struct TestClusterCreator; +impl ClusterCreator for TestClusterCreator { + fn create_new_cluster( + _cluster_id: ClusterId, + _cluster_manager_id: T::AccountId, + _cluster_reserve_id: T::AccountId, + _cluster_params: ClusterParams, + _cluster_gov_params: ClusterGovParams, + ) -> DispatchResult { + Ok(()) + } +} + +pub struct TestCustomerDepositor; +impl CustomerDepositor for TestCustomerDepositor { + fn deposit(_customer: T::AccountId, _amount: u128) -> Result<(), DispatchError> { + Ok(()) + } + fn deposit_extra(_customer: T::AccountId, _amount: u128) -> Result<(), DispatchError> { + Ok(()) + } +} + pub const RESERVE_ACCOUNT_ID: AccountId = 999; pub const TREASURY_ACCOUNT_ID: AccountId = 888; pub const VALIDATOR1_ACCOUNT_ID: AccountId = 111; @@ -261,9 +290,6 @@ pub fn get_fees(cluster_id: &ClusterId) -> Result ClusterVisitor for TestClusterVisitor { - fn cluster_has_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { - true - } fn ensure_cluster(_cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { Ok(()) } @@ -306,6 +332,12 @@ impl ClusterVisitor for TestClusterVisitor { let reserve_account = RESERVE_ACCOUNT_ID.to_ne_bytes(); Ok(T::AccountId::decode(&mut &reserve_account[..]).unwrap()) } + + fn get_bonding_params( + _cluster_id: &ClusterId, + ) -> Result, ClusterVisitorError> { + unimplemented!() + } } pub(crate) type TestRuntimeCall = ::RuntimeCall; diff --git a/pallets/ddc-payouts/src/weights.rs b/pallets/ddc-payouts/src/weights.rs new file mode 100644 index 000000000..e4764f492 --- /dev/null +++ b/pallets/ddc-payouts/src/weights.rs @@ -0,0 +1,210 @@ +//! Autogenerated weights for pallet_ddc_payouts +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Yahors-MacBook-Pro.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/debug/cere +// benchmark +// pallet +// --chain=dev +// --execution=wasm +// --pallet=pallet-ddc-payouts +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --template=./.maintain/frame-weight-template-clippy.hbs +// --output=pallets/ddc-payouts/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_ddc_payouts. +pub trait WeightInfo { + fn set_authorised_caller() -> Weight; + fn begin_billing_report() -> Weight; + fn begin_charging_customers() -> Weight; + fn send_charging_customers_batch(b: u32, ) -> Weight; + fn end_charging_customers() -> Weight; + fn begin_rewarding_providers() -> Weight; + fn send_rewarding_providers_batch(b: u32, ) -> Weight; + fn end_rewarding_providers() -> Weight; + fn end_billing_report() -> Weight; +} + +/// Weights for pallet_ddc_payouts using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: DdcPayouts AuthorisedCaller (r:0 w:1) + fn set_authorised_caller() -> Weight { + Weight::from_ref_time(251_000_000_u64) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn begin_billing_report() -> Weight { + Weight::from_ref_time(466_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn begin_charging_customers() -> Weight { + Weight::from_ref_time(440_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:2 w:2) + // Storage: DdcPayouts DebtorCustomers (r:1 w:1) + /// The range of component `b` is `[1, 1000]`. + fn send_charging_customers_batch(b: u32, ) -> Weight { + Weight::from_ref_time(1_267_000_000_u64) + // Standard Error: 3_691_054 + .saturating_add(Weight::from_ref_time(557_422_673_u64).saturating_mul(b as u64)) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(b as u64))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(b as u64))) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) + // Storage: System Account (r:3 w:3) + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: Staking CounterForValidators (r:1 w:0) + // Storage: Staking Validators (r:2 w:0) + fn end_charging_customers() -> Weight { + Weight::from_ref_time(1_978_000_000_u64) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn begin_rewarding_providers() -> Weight { + Weight::from_ref_time(446_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: System Account (r:2 w:2) + /// The range of component `b` is `[1, 1000]`. + fn send_rewarding_providers_batch(b: u32, ) -> Weight { + Weight::from_ref_time(758_000_000_u64) + // Standard Error: 148_210 + .saturating_add(Weight::from_ref_time(336_218_526_u64).saturating_mul(b as u64)) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(b as u64))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(b as u64))) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn end_rewarding_providers() -> Weight { + Weight::from_ref_time(458_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn end_billing_report() -> Weight { + Weight::from_ref_time(449_000_000_u64) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: DdcPayouts AuthorisedCaller (r:0 w:1) + fn set_authorised_caller() -> Weight { + Weight::from_ref_time(251_000_000_u64) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn begin_billing_report() -> Weight { + Weight::from_ref_time(466_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn begin_charging_customers() -> Weight { + Weight::from_ref_time(440_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) + // Storage: DdcCustomers Ledger (r:1 w:1) + // Storage: System Account (r:2 w:2) + // Storage: DdcPayouts DebtorCustomers (r:1 w:1) + /// The range of component `b` is `[1, 1000]`. + fn send_charging_customers_batch(b: u32, ) -> Weight { + Weight::from_ref_time(1_267_000_000_u64) + // Standard Error: 3_691_054 + .saturating_add(Weight::from_ref_time(557_422_673_u64).saturating_mul(b as u64)) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(b as u64))) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(b as u64))) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) + // Storage: System Account (r:3 w:3) + // Storage: DdcClusters Clusters (r:1 w:0) + // Storage: Staking CounterForValidators (r:1 w:0) + // Storage: Staking Validators (r:2 w:0) + fn end_charging_customers() -> Weight { + Weight::from_ref_time(1_978_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(10_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn begin_rewarding_providers() -> Weight { + Weight::from_ref_time(446_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + // Storage: System Account (r:2 w:2) + /// The range of component `b` is `[1, 1000]`. + fn send_rewarding_providers_batch(b: u32, ) -> Weight { + Weight::from_ref_time(758_000_000_u64) + // Standard Error: 148_210 + .saturating_add(Weight::from_ref_time(336_218_526_u64).saturating_mul(b as u64)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(b as u64))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(b as u64))) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn end_rewarding_providers() -> Weight { + Weight::from_ref_time(458_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: DdcPayouts AuthorisedCaller (r:1 w:0) + // Storage: DdcPayouts ActiveBillingReports (r:1 w:1) + fn end_billing_report() -> Weight { + Weight::from_ref_time(449_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/pallets/ddc-staking/Cargo.toml b/pallets/ddc-staking/Cargo.toml index 67dddf18a..ac2bfaebc 100644 --- a/pallets/ddc-staking/Cargo.toml +++ b/pallets/ddc-staking/Cargo.toml @@ -16,8 +16,10 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/su sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } [dev-dependencies] +lazy_static = "1.4.0" pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } pallet-timestamp = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } +parking_lot = "0.12.1" sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } sp-tracing = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } substrate-test-utils = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } diff --git a/pallets/ddc-staking/src/benchmarking.rs b/pallets/ddc-staking/src/benchmarking.rs index 6353e4a5d..37eb110a1 100644 --- a/pallets/ddc-staking/src/benchmarking.rs +++ b/pallets/ddc-staking/src/benchmarking.rs @@ -2,7 +2,7 @@ use super::*; use crate::Pallet as DdcStaking; -use ddc_primitives::{CDNNodePubKey, NodeType}; +use ddc_primitives::{CDNNodeParams, CDNNodePubKey, NodeParams, NodeType, StorageNodePubKey}; use testing_utils::*; use frame_support::traits::Currency; @@ -23,6 +23,16 @@ benchmarks! { let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); let node = NodePubKey::CDNPubKey(CDNNodePubKey::new([0; 32])); + let _ = T::NodeCreator::create_node( + node.clone(), + stash.clone(), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }) + )?; let amount = T::Currency::minimum_balance() * 10u32.into(); whitelist_account!(stash); }: _(RawOrigin::Signed(stash.clone()), controller_lookup, node.clone(), amount) @@ -65,7 +75,8 @@ benchmarks! { } store { - let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::CDN).unwrap_or(10u128).saturated_into::>())?; + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([0; 32])); + let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::CDN).unwrap_or(100u128), node_pub_key)?; whitelist_account!(controller); }: _(RawOrigin::Signed(controller), ClusterId::from([1; 20])) @@ -74,7 +85,8 @@ benchmarks! { } serve { - let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::CDN).unwrap_or(10u128).saturated_into::>())?; + let node_pub_key = NodePubKey::CDNPubKey(CDNNodePubKey::new([0; 32])); + let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::CDN).unwrap_or(10u128), node_pub_key)?; whitelist_account!(controller); }: _(RawOrigin::Signed(controller), ClusterId::from([1; 20])) @@ -86,7 +98,8 @@ benchmarks! { // clean up any existing state. clear_storages_and_cdns::(); - let (cdn_stash, cdn_controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::CDN).unwrap_or(10u128).saturated_into::>())?; + let node_pub_key = NodePubKey::CDNPubKey(CDNNodePubKey::new([0; 32])); + let (cdn_stash, cdn_controller, _) = create_stash_controller_node_with_balance::(0, T::ClusterVisitor::get_bond_size(&ClusterId::from([1; 20]), NodeType::CDN).unwrap_or(10u128), node_pub_key)?; DdcStaking::::serve(RawOrigin::Signed(cdn_controller.clone()).into(), ClusterId::from([1; 20]))?; assert!(CDNs::::contains_key(&cdn_stash)); frame_system::Pallet::::set_block_number(T::BlockNumber::from(1u32)); @@ -117,4 +130,10 @@ benchmarks! { verify { assert!(Nodes::::contains_key(&new_node)); } + + impl_benchmark_test_suite!( + DdcStaking, + crate::mock::ExtBuilder::default().build(), + crate::mock::Test, + ); } diff --git a/pallets/ddc-staking/src/lib.rs b/pallets/ddc-staking/src/lib.rs index c7efa74c7..d7bbad30e 100644 --- a/pallets/ddc-staking/src/lib.rs +++ b/pallets/ddc-staking/src/lib.rs @@ -31,9 +31,9 @@ use crate::weights::WeightInfo; use codec::{Decode, Encode, HasCompact}; pub use ddc_primitives::{ClusterId, NodePubKey, NodeType}; use ddc_traits::{ - cluster::{ClusterVisitor, ClusterVisitorError}, - node::NodeVisitor, - staking::{StakingVisitor, StakingVisitorError}, + cluster::{ClusterCreator, ClusterVisitor, ClusterVisitorError}, + node::{NodeCreator, NodeVisitor}, + staking::{StakerCreator, StakingVisitor, StakingVisitorError}, }; use frame_support::{ @@ -141,6 +141,8 @@ impl< #[frame_support::pallet] pub mod pallet { + use ddc_traits::{cluster::ClusterManager, node::NodeVisitorError}; + use super::*; #[pallet::pallet] @@ -159,7 +161,13 @@ pub mod pallet { type ClusterVisitor: ClusterVisitor; + type ClusterCreator: ClusterCreator>; + + type ClusterManager: ClusterManager; + type NodeVisitor: NodeVisitor; + + type NodeCreator: NodeCreator; } /// Map from all locked "stash" accounts to the controller account. @@ -195,6 +203,16 @@ pub mod pallet { #[pallet::getter(fn providers)] pub type Providers = StorageMap<_, Twox64Concat, T::AccountId, NodePubKey>; + /// Map of Storage node provider stash accounts that aim to leave a cluster + #[pallet::storage] + #[pallet::getter(fn leaving_storages)] + pub type LeavingStorages = StorageMap<_, Twox64Concat, T::AccountId, ClusterId>; + + // Map of CDN node provider stash accounts that aim to leave a cluster + #[pallet::storage] + #[pallet::getter(fn leaving_cdns)] + pub type LeavingCDNs = StorageMap<_, Twox64Concat, T::AccountId, ClusterId>; + #[pallet::genesis_config] pub struct GenesisConfig { #[allow(clippy::type_complexity)] @@ -273,6 +291,12 @@ pub mod pallet { /// An account that started participating as either a storage network or CDN participant. /// \[stash\] Activated(T::AccountId), + /// An account that started unbonding tokens below the minimum value set for the cluster + /// his CDN or Storage node is assigned to \[stash\] + LeaveSoon(T::AccountId), + /// An account that unbonded tokens below the minimum value set for the cluster his + /// CDN or Storage node was assigned to \[stash\] + Left(T::AccountId), } #[pallet::error] @@ -302,6 +326,8 @@ pub mod pallet { NotNodeController, /// No stake found associated with the provided node. NodeHasNoStake, + /// No cluster found + NoCluster, /// No cluster governance params found for cluster NoClusterGovParams, /// Conditions for fast chill are not met, try the regular `chill` from @@ -314,6 +340,11 @@ pub mod pallet { ArithmeticOverflow, /// Arithmetic underflow occurred ArithmeticUnderflow, + /// Attempt to associate stake with non-existing node + NodeIsNotFound, + /// Action is prohibited for a node provider stash account that is in the process of + /// leaving a cluster + NodeIsLeaving, } #[pallet::call] @@ -355,6 +386,9 @@ pub mod pallet { Err(Error::::AlreadyPaired)? } + // Checks that the node is registered in the network + ensure!(T::NodeVisitor::exists(&node), Error::::NodeIsNotFound); + frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; Nodes::::insert(&node, &stash); @@ -404,6 +438,7 @@ pub mod pallet { ) -> DispatchResult { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; + ensure!( ledger.unlocking.len() < MaxUnlockingChunks::get() as usize, Error::::NoMoreChunks, @@ -432,6 +467,8 @@ pub mod pallet { .map_err(Into::>::into)?; bond_size.saturated_into::>() } else { + // If node is not assigned to a cluster or node is chilling, allow to unbond + // any available amount. Zero::zero() }; @@ -439,36 +476,50 @@ pub mod pallet { // cluster. If a user runs into this error, they should chill first. ensure!(ledger.active >= min_active_bond, Error::::InsufficientBond); - let unbonding_delay_in_blocks = if let Some(cluster_id) = Self::cdns(&ledger.stash) - { - T::ClusterVisitor::get_unbonding_delay(&cluster_id, NodeType::CDN) - .map_err(Into::>::into)? - } else if let Some(cluster_id) = Self::storages(&ledger.stash) { - T::ClusterVisitor::get_unbonding_delay(&cluster_id, NodeType::Storage) - .map_err(Into::>::into)? - } else { - let node_pub_key = - >::get(&ledger.stash).ok_or(Error::::BadState)?; + let node_pub_key = + >::get(&ledger.stash).ok_or(Error::::BadState)?; + + let unbonding_delay = if T::NodeVisitor::exists(&node_pub_key) { + let node_cluster_id = T::NodeVisitor::get_cluster_id(&node_pub_key) + .map_err(Into::>::into)?; + + if let Some(cluster_id) = node_cluster_id { + let bonding_params = T::ClusterVisitor::get_bonding_params(&cluster_id) + .map_err(Into::>::into)?; + + let min_bond_size = match node_pub_key { + NodePubKey::CDNPubKey(_) => bonding_params.cdn_bond_size, + NodePubKey::StoragePubKey(_) => bonding_params.storage_bond_size, + }; + + // If provider is trying to unbond after chilling and aims to leave the + // cluster eventually, we keep its stake till the end of unbonding period. + if ledger.active < min_bond_size.saturated_into::>() { + match node_pub_key { + NodePubKey::CDNPubKey(_) => + LeavingCDNs::::insert(ledger.stash.clone(), cluster_id), + NodePubKey::StoragePubKey(_) => + LeavingStorages::::insert(ledger.stash.clone(), cluster_id), + }; + + Self::deposit_event(Event::::LeaveSoon(ledger.stash.clone())); + }; - if let Ok(Some(cluster_id)) = T::NodeVisitor::get_cluster_id(&node_pub_key) { match node_pub_key { - NodePubKey::CDNPubKey(_) => - T::ClusterVisitor::get_unbonding_delay(&cluster_id, NodeType::CDN) - .map_err(Into::>::into)?, - NodePubKey::StoragePubKey(_) => T::ClusterVisitor::get_unbonding_delay( - &cluster_id, - NodeType::Storage, - ) - .map_err(Into::>::into)?, + NodePubKey::CDNPubKey(_) => bonding_params.cdn_unbonding_delay, + NodePubKey::StoragePubKey(_) => bonding_params.storage_unbonding_delay, } } else { // If node is not a member of any cluster, allow immediate unbonding. T::BlockNumber::from(0u32) } + } else { + // If node was deleted, allow immediate unbonding. + T::BlockNumber::from(0u32) }; // block number + configuration -> no overflow - let block = >::block_number() + unbonding_delay_in_blocks; + let block = >::block_number() + unbonding_delay; if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.block == block) { @@ -505,6 +556,7 @@ pub mod pallet { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; let (stash, old_total) = (ledger.stash.clone(), ledger.total); + let node_pub_key = >::get(stash.clone()).ok_or(Error::::BadState)?; ledger = ledger.consolidate_unlocked(>::block_number()); @@ -526,7 +578,22 @@ pub mod pallet { // Already checked that this won't overflow by entry condition. let value = old_total.checked_sub(&ledger.total).ok_or(Error::::ArithmeticUnderflow)?; - Self::deposit_event(Event::::Withdrawn(stash, value)); + Self::deposit_event(Event::::Withdrawn(stash.clone(), value)); + + // If provider aimed to leave the cluster and the unbonding period ends, remove + // the node from the cluster + if let Some(cluster_id) = + >::get(&stash).or_else(|| >::get(&stash)) + { + // Cluster manager could remove the node from cluster by this moment already, so + // it is ok to ignore result. + let _ = T::ClusterManager::remove_node(&cluster_id, &node_pub_key); + + >::remove(&stash); + >::remove(&stash); + + Self::deposit_event(Event::::Left(stash)); + } } Ok(()) @@ -573,6 +640,10 @@ pub mod pallet { // Cancel previous "chill" attempts Self::reset_chilling(&controller); return Ok(()) + } else { + // Can't participate in new CDN network if provider hasn't left the previous cluster + // yet + ensure!(!LeavingCDNs::::contains_key(stash), Error::::NodeIsLeaving); } Self::do_add_cdn(stash, cluster_id); @@ -621,6 +692,10 @@ pub mod pallet { // Cancel previous "chill" attempts Self::reset_chilling(&controller); return Ok(()) + } else { + // Can't participate in new Storage network if provider hasn't left the previous + // cluster yet + ensure!(!LeavingStorages::::contains_key(stash), Error::::NodeIsLeaving); } Self::do_add_storage(stash, cluster_id); @@ -661,7 +736,7 @@ pub mod pallet { .map_err(Into::>::into)?; (cluster, chill_delay) } else { - return Ok(()) // already chilled + return Ok(()) // node is already chilling or leaving the cluster }; if delay == T::BlockNumber::from(0u32) { @@ -741,6 +816,11 @@ pub mod pallet { ensure!(!>::contains_key(&stash), Error::::AlreadyInRole); ensure!(!>::contains_key(&stash), Error::::AlreadyInRole); + // Ensure that provider is not about leaving the cluster as it may cause the removal + // of an unexpected node after unbonding. + ensure!(!>::contains_key(&stash), Error::::NodeIsLeaving); + ensure!(!>::contains_key(&stash), Error::::NodeIsLeaving); + >::insert(new_node.clone(), stash.clone()); >::insert(stash, new_node); @@ -763,7 +843,7 @@ pub mod pallet { .or_else(|| >::get(&stash)) .ok_or(Error::::NodeHasNoStake)?; - let is_cluster_node = T::ClusterVisitor::cluster_has_node(&cluster_id, &node_pub_key); + let is_cluster_node = T::ClusterManager::contains_node(&cluster_id, &node_pub_key); ensure!(!is_cluster_node, Error::::FastChillProhibited); // block number + 1 => no overflow @@ -879,8 +959,39 @@ pub mod pallet { } } + impl StakerCreator> for Pallet { + fn bond_stake_and_participate( + stash: T::AccountId, + controller: T::AccountId, + node: NodePubKey, + value: BalanceOf, + cluster_id: ClusterId, + ) -> DispatchResult { + Nodes::::insert(&node, &stash); + Providers::::insert(&stash, &node); + >::insert(&stash, &controller); + let stash_balance = T::Currency::free_balance(&stash); + let value = value.min(stash_balance); + Self::deposit_event(Event::::Bonded(stash.clone(), value)); + let item = StakingLedger { + stash: stash.clone(), + total: value, + active: value, + chilling: Default::default(), + unlocking: Default::default(), + }; + Self::update_ledger(&controller, &item); + match node { + NodePubKey::StoragePubKey(_node) => Self::do_add_storage(&stash, cluster_id), + NodePubKey::CDNPubKey(_node) => Self::do_add_cdn(&stash, cluster_id), + } + + Ok(()) + } + } + impl StakingVisitor for Pallet { - fn node_has_stake( + fn has_activated_stake( node_pub_key: &NodePubKey, cluster_id: &ClusterId, ) -> Result { @@ -889,34 +1000,46 @@ pub mod pallet { let maybe_cdn_in_cluster = CDNs::::get(&stash); let maybe_storage_in_cluster = Storages::::get(&stash); - let has_stake: bool = maybe_cdn_in_cluster + let has_activated_stake: bool = maybe_cdn_in_cluster .or(maybe_storage_in_cluster) .is_some_and(|staking_cluster| staking_cluster == *cluster_id); - Ok(has_stake) + Ok(has_activated_stake) } - fn node_is_chilling(node_pub_key: &NodePubKey) -> Result { + fn has_stake(node_pub_key: &NodePubKey) -> bool { + >::get(node_pub_key).is_some() + } + + fn has_chilling_attempt(node_pub_key: &NodePubKey) -> Result { let stash = >::get(node_pub_key).ok_or(StakingVisitorError::NodeStakeDoesNotExist)?; let controller = >::get(&stash).ok_or(StakingVisitorError::NodeStakeIsInBadState)?; - let is_chilling = >::get(&controller) + let is_chilling_attempt = >::get(&controller) .ok_or(StakingVisitorError::NodeStakeIsInBadState)? .chilling .is_some(); - Ok(is_chilling) + Ok(is_chilling_attempt) } } impl From for Error { fn from(error: ClusterVisitorError) -> Self { match error { - ClusterVisitorError::ClusterDoesNotExist => Error::::NodeHasNoStake, + ClusterVisitorError::ClusterDoesNotExist => Error::::NoCluster, ClusterVisitorError::ClusterGovParamsNotSet => Error::::NoClusterGovParams, } } } + + impl From for Error { + fn from(error: NodeVisitorError) -> Self { + match error { + NodeVisitorError::NodeDoesNotExist => Error::::NodeIsNotFound, + } + } + } } diff --git a/pallets/ddc-staking/src/mock.rs b/pallets/ddc-staking/src/mock.rs index 9085b5ed5..c1aa469f5 100644 --- a/pallets/ddc-staking/src/mock.rs +++ b/pallets/ddc-staking/src/mock.rs @@ -3,18 +3,25 @@ #![allow(dead_code)] use crate::{self as pallet_ddc_staking, *}; -use ddc_primitives::{CDNNodePubKey, ClusterFeesParams, ClusterPricingParams, StorageNodePubKey}; +use ddc_primitives::{ + CDNNodePubKey, ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterParams, + ClusterPricingParams, NodeParams, NodePubKey, StorageNodePubKey, +}; use ddc_traits::{ - cluster::{ClusterVisitor, ClusterVisitorError}, + cluster::{ClusterManager, ClusterManagerError, ClusterVisitor, ClusterVisitorError}, node::{NodeVisitor, NodeVisitorError}, }; use frame_support::{ construct_runtime, + dispatch::DispatchResult, traits::{ConstU32, ConstU64, Everything, GenesisBuild}, weights::constants::RocksDbWeight, }; + use frame_system::mocking::{MockBlock, MockUncheckedExtrinsic}; +use lazy_static::lazy_static; +use parking_lot::{ReentrantMutex, ReentrantMutexGuard}; use sp_core::H256; use sp_io::TestExternalities; use sp_runtime::{ @@ -23,6 +30,7 @@ use sp_runtime::{ Perquintill, }; use sp_std::collections::btree_map::BTreeMap; +use std::cell::RefCell; /// The AccountId alias in this test module. pub(crate) type AccountId = u64; @@ -101,16 +109,41 @@ impl crate::pallet::Config for Test { type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type ClusterVisitor = TestClusterVisitor; - type NodeVisitor = TestNodeVisitor; + type ClusterManager = TestClusterManager; + type NodeVisitor = MockNodeVisitor; + type NodeCreator = TestNodeCreator; + type ClusterCreator = TestClusterCreator; } pub(crate) type DdcStakingCall = crate::Call; pub(crate) type TestRuntimeCall = ::RuntimeCall; +pub struct TestNodeCreator; +pub struct TestClusterCreator; pub struct TestClusterVisitor; -impl ClusterVisitor for TestClusterVisitor { - fn cluster_has_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { - true + +impl NodeCreator for TestNodeCreator { + fn create_node( + _node_pub_key: NodePubKey, + _provider_id: T::AccountId, + _node_params: NodeParams, + ) -> DispatchResult { + Ok(()) } +} + +impl ClusterCreator for TestClusterCreator { + fn create_new_cluster( + _cluster_id: ClusterId, + _cluster_manager_id: T::AccountId, + _cluster_reserve_id: T::AccountId, + _cluster_params: ClusterParams, + _cluster_gov_params: ClusterGovParams, + ) -> DispatchResult { + Ok(()) + } +} + +impl ClusterVisitor for TestClusterVisitor { fn ensure_cluster(_cluster_id: &ClusterId) -> Result<(), ClusterVisitorError> { Ok(()) } @@ -157,12 +190,113 @@ impl ClusterVisitor for TestClusterVisitor { ) -> Result { Err(ClusterVisitorError::ClusterDoesNotExist) } + + fn get_bonding_params( + cluster_id: &ClusterId, + ) -> Result, ClusterVisitorError> { + Ok(ClusterBondingParams { + cdn_bond_size: >::get_bond_size( + cluster_id, + NodeType::CDN, + ) + .unwrap_or_default(), + cdn_chill_delay: >::get_chill_delay( + cluster_id, + NodeType::CDN, + ) + .unwrap_or_default(), + cdn_unbonding_delay: >::get_unbonding_delay( + cluster_id, + NodeType::CDN, + ) + .unwrap_or_default(), + storage_bond_size: >::get_bond_size( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + storage_chill_delay: >::get_chill_delay( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + storage_unbonding_delay: + >::get_unbonding_delay( + cluster_id, + NodeType::Storage, + ) + .unwrap_or_default(), + }) + } } -pub struct TestNodeVisitor; -impl NodeVisitor for TestNodeVisitor { +pub struct TestClusterManager; +impl ClusterManager for TestClusterManager { + fn contains_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { + true + } + + fn add_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError> { + Ok(()) + } + + fn remove_node( + _cluster_id: &ClusterId, + _node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError> { + Ok(()) + } +} + +lazy_static! { + // We have to use the ReentrantMutex as every test's thread that needs to perform some configuration on the mock acquires the lock at least 2 times: + // the first time when the mock configuration happens, and + // the second time when the pallet calls the MockNodeVisitor during execution + static ref MOCK_NODE: ReentrantMutex> = + ReentrantMutex::new(RefCell::new(MockNode::default())); +} + +pub struct MockNode { + pub cluster_id: Option, + pub exists: bool, +} + +impl Default for MockNode { + fn default() -> Self { + Self { cluster_id: None, exists: true } + } +} + +pub struct MockNodeVisitor; + +impl MockNodeVisitor { + // Every test's thread must hold the lock till the end of its test + pub fn set_and_hold_lock(mock: MockNode) -> ReentrantMutexGuard<'static, RefCell> { + let lock = MOCK_NODE.lock(); + *lock.borrow_mut() = mock; + lock + } + + // Every test's thread must release the lock that it previously acquired in the end of its + // test + pub fn reset_and_release_lock(lock: ReentrantMutexGuard<'static, RefCell>) { + *lock.borrow_mut() = MockNode::default(); + } +} + +impl NodeVisitor for MockNodeVisitor { fn get_cluster_id(_node_pub_key: &NodePubKey) -> Result, NodeVisitorError> { - Ok(None) + let lock = MOCK_NODE.lock(); + let mock_ref = lock.borrow(); + Ok(mock_ref.cluster_id) + } + fn exists(_node_pub_key: &NodePubKey) -> bool { + let lock = MOCK_NODE.lock(); + let mock_ref = lock.borrow(); + mock_ref.exists } } @@ -219,7 +353,7 @@ impl ExtBuilder { self.storages.push((stash, controller, stake, cluster)); self } - fn build(self) -> TestExternalities { + pub fn build(self) -> TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); diff --git a/pallets/ddc-staking/src/testing_utils.rs b/pallets/ddc-staking/src/testing_utils.rs index 58e6b656b..7dec075cc 100644 --- a/pallets/ddc-staking/src/testing_utils.rs +++ b/pallets/ddc-staking/src/testing_utils.rs @@ -1,12 +1,16 @@ //! Testing utils for ddc-staking. use crate::{Pallet as DdcStaking, *}; -use ddc_primitives::CDNNodePubKey; +use ddc_primitives::{ + CDNNodeParams, CDNNodePubKey, ClusterGovParams, ClusterId, ClusterParams, NodeParams, + StorageNodeParams, +}; + use frame_benchmarking::account; use frame_system::RawOrigin; use frame_support::traits::Currency; -use sp_runtime::traits::StaticLookup; +use sp_runtime::{traits::StaticLookup, Perquintill}; use sp_std::prelude::*; const SEED: u32 = 0; @@ -36,9 +40,10 @@ pub fn create_funded_user( pub fn create_funded_user_with_balance( string: &'static str, n: u32, - balance: BalanceOf, + balance_factor: u128, ) -> T::AccountId { let user = account(string, n, SEED); + let balance = T::Currency::minimum_balance() * balance_factor.saturated_into::>(); let _ = T::Currency::make_free_balance_be(&user, balance); user } @@ -53,6 +58,17 @@ pub fn create_stash_controller_node( let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); let node = NodePubKey::CDNPubKey(CDNNodePubKey::new([0; 32])); + + T::NodeCreator::create_node( + node.clone(), + stash.clone(), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }), + )?; let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); DdcStaking::::bond( RawOrigin::Signed(stash.clone()).into(), @@ -66,19 +82,73 @@ pub fn create_stash_controller_node( /// Create a stash and controller pair with fixed balance. pub fn create_stash_controller_node_with_balance( n: u32, - balance: crate::BalanceOf, + balance_factor: u128, + node_pub_key: NodePubKey, ) -> Result<(T::AccountId, T::AccountId, NodePubKey), &'static str> { - let stash = create_funded_user_with_balance::("stash", n, balance); - let controller = create_funded_user_with_balance::("controller", n, balance); + let stash = create_funded_user_with_balance::("stash", n, balance_factor); + let controller = create_funded_user_with_balance::("controller", n, balance_factor); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); - let node = NodePubKey::CDNPubKey(CDNNodePubKey::new([0; 32])); + + let node_pub = node_pub_key.clone(); + match node_pub_key { + NodePubKey::CDNPubKey(node_pub_key) => { + T::NodeCreator::create_node( + ddc_primitives::NodePubKey::CDNPubKey(node_pub_key), + stash.clone(), + NodeParams::CDNParams(CDNNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }), + )?; + }, + NodePubKey::StoragePubKey(node_pub_key) => { + T::NodeCreator::create_node( + NodePubKey::StoragePubKey(node_pub_key), + stash.clone(), + NodeParams::StorageParams(StorageNodeParams { + host: vec![1u8, 255], + http_port: 35000u16, + grpc_port: 25000u16, + p2p_port: 15000u16, + }), + )?; + }, + } + + let cluster_id = ClusterId::from([1; 20]); + let cluster_params = ClusterParams { node_provider_auth_contract: Some(stash.clone()) }; + let cluster_gov_params: ClusterGovParams, T::BlockNumber> = ClusterGovParams { + treasury_share: Perquintill::default(), + validators_share: Perquintill::default(), + cluster_reserve_share: Perquintill::default(), + cdn_bond_size: 10u32.into(), + cdn_chill_delay: 50u32.into(), + cdn_unbonding_delay: 50u32.into(), + storage_bond_size: 10u32.into(), + storage_chill_delay: 50u32.into(), + storage_unbonding_delay: 50u32.into(), + unit_per_mb_stored: 10, + unit_per_mb_streamed: 10, + unit_per_put_request: 10, + unit_per_get_request: 10, + }; + T::ClusterCreator::create_new_cluster( + cluster_id, + stash.clone(), + stash.clone(), + cluster_params, + cluster_gov_params, + )?; DdcStaking::::bond( RawOrigin::Signed(stash.clone()).into(), controller_lookup, - node.clone(), - balance, + node_pub.clone(), + T::Currency::minimum_balance() * balance_factor.saturated_into::>(), )?; - Ok((stash, controller, node)) + + Ok((stash, controller, node_pub)) } diff --git a/pallets/ddc-staking/src/tests.rs b/pallets/ddc-staking/src/tests.rs index d0756f3d4..358b736ce 100644 --- a/pallets/ddc-staking/src/tests.rs +++ b/pallets/ddc-staking/src/tests.rs @@ -1,7 +1,7 @@ //! Tests for the module. use super::{mock::*, *}; -use ddc_primitives::CDNNodePubKey; +use ddc_primitives::{CDNNodePubKey, StorageNodePubKey}; use frame_support::{assert_noop, assert_ok, traits::ReservableCurrency}; use pallet_balances::Error as BalancesError; @@ -9,6 +9,21 @@ use pallet_balances::Error as BalancesError; pub const BLOCK_TIME: u64 = 1000; pub const INIT_TIMESTAMP: u64 = 30_000; +#[test] +fn test_default_staking_ledger() { + // Verifies initial conditions of mock + ExtBuilder::default().build_and_execute(|| { + let default_staking_ledger = StakingLedger::< + ::AccountId, + BalanceOf, + Test, + >::default_from(1); + // Account 11 is stashed and locked, and account 10 is the controller + assert_eq!(default_staking_ledger.stash, 1); + assert_eq!(default_staking_ledger.total, Zero::zero()); + }); +} + #[test] fn basic_setup_works() { // Verifies initial conditions of mock @@ -58,6 +73,10 @@ fn change_controller_works() { // Change controller. assert_ok!(DdcStaking::set_controller(RuntimeOrigin::signed(11), 3)); + assert_noop!( + DdcStaking::set_controller(RuntimeOrigin::signed(11), 3), + Error::::AlreadyPaired + ); assert_eq!(DdcStaking::bonded(&11), Some(3)); // 10 is no longer in control. @@ -89,6 +108,20 @@ fn not_enough_inital_bond_flow() { Error::::InsufficientBond ); + // Add new Storage participant, account 1 controlled by 2 with node 3. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(1), + 2, + NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), + 100 + )); + + // Not enough tokens bonded to store + assert_noop!( + DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20])), + Error::::InsufficientBond + ); + // Can not bond extra assert_noop!( DdcStaking::bond( @@ -121,6 +154,67 @@ fn not_enough_inital_bond_flow() { }) } +#[test] +fn unbonding_edge_cases_work() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + // Add new CDN participant, account 3 controlled by 4 with node 5. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(3), + 4, + NodePubKey::CDNPubKey(CDNNodePubKey::new([5; 32])), + 100 + )); + + assert_ok!(DdcStaking::serve(RuntimeOrigin::signed(4), ClusterId::from([1; 20]))); + + assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(4), 1)); + while System::block_number() < 33 { + assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(4), 1)); + System::assert_last_event(Event::Unbonded(3, 1).into()); + System::set_block_number(System::block_number() + 1); + } + + assert_noop!(DdcStaking::unbond(RuntimeOrigin::signed(4), 1), Error::::NoMoreChunks); + }) +} + +#[test] +fn serve_or_store_should_be_prohibited() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + // Add new CDN participant, account 3 controlled by 4 with node 5. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(3), + 4, + NodePubKey::CDNPubKey(CDNNodePubKey::new([5; 32])), + 100 + )); + + // Add new Storage participant, account 1 controlled by 2 with node 3. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(1), + 2, + NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), + 100 + )); + + // Not enough tokens bonded to serve + assert_noop!( + DdcStaking::serve(RuntimeOrigin::signed(2), ClusterId::from([1; 20])), + Error::::ServingProhibited + ); + + // Not enough tokens bonded to store + assert_noop!( + DdcStaking::store(RuntimeOrigin::signed(4), ClusterId::from([1; 20])), + Error::::StoringProhibited + ); + }) +} + #[test] fn set_node_works() { ExtBuilder::default().build_and_execute(|| { @@ -160,6 +254,45 @@ fn set_node_works() { }) } +#[test] +fn cancel_previous_chill_works() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + let cluster_id = ClusterId::from([1; 20]); + // Add new CDN participant, account 3 controlled by 4 with node 5. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(3), + 4, + NodePubKey::CDNPubKey(CDNNodePubKey::new([5; 32])), + 100 + )); + + // Add new Storage participant, account 1 controlled by 2 with node 3. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(1), + 2, + NodePubKey::StoragePubKey(StorageNodePubKey::new([3; 32])), + 100 + )); + + // Not enough tokens bonded to serve + assert_ok!(DdcStaking::serve(RuntimeOrigin::signed(4), cluster_id)); + + assert_ok!(DdcStaking::store(RuntimeOrigin::signed(2), ClusterId::from([1; 20]))); + + // Schedule CDN participant removal. + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(4))); + // Not enough tokens bonded to serve + assert_ok!(DdcStaking::serve(RuntimeOrigin::signed(4), cluster_id)); + + // Schedule CDN participant removal. + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(2))); + // Not enough tokens bonded to serve + assert_ok!(DdcStaking::store(RuntimeOrigin::signed(2), cluster_id)); + }) +} + #[test] fn staking_should_work() { ExtBuilder::default().build_and_execute(|| { @@ -290,3 +423,227 @@ fn staking_should_work() { assert_eq!(DdcStaking::cdns(3), None); }); } + +#[test] +fn cdn_full_unbonding_works() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + let provider_stash: u64 = 1; + let provider_controller: u64 = 2; + let cluster_id = ClusterId::from([1; 20]); + let node_pub_key = NodePubKey::CDNPubKey(CDNNodePubKey::new([1; 32])); + + let lock = MockNodeVisitor::set_and_hold_lock(MockNode { + cluster_id: Some(cluster_id), + exists: true, + }); + + let cdn_bond_size = 10_u128; + let cdn_chill_delay = 10_u64; + let cdn_unbond_delay = 10_u64; + + // Put some money in account that we'll use. + let _ = Balances::make_free_balance_be(&provider_controller, 2000); + let _ = Balances::make_free_balance_be(&provider_stash, 2000); + + // Add new CDN participant, account 1 controlled by 2 with node 1. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(provider_stash), + provider_controller, + node_pub_key.clone(), + cdn_bond_size, // min bond size + )); + System::assert_last_event(Event::Bonded(provider_stash, cdn_bond_size).into()); + assert_ok!(DdcStaking::serve(RuntimeOrigin::signed(provider_controller), cluster_id)); + System::assert_last_event(Event::Activated(provider_stash).into()); + + assert_eq!(DdcStaking::cdns(provider_stash), Some(cluster_id)); + assert_eq!(DdcStaking::nodes(node_pub_key), Some(provider_stash)); + + // Set block timestamp. + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + + // Schedule CDN participant removal. + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller))); + let chilling = System::block_number() + cdn_chill_delay; + System::assert_last_event(Event::ChillSoon(provider_stash, cluster_id, chilling).into()); + + // Set the block number that allows us to chill. + while System::block_number() < chilling { + System::set_block_number(System::block_number() + 1); + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + } + + // Actual CDN participant removal. + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller))); + System::assert_last_event(Event::Chilled(provider_stash).into()); + + // Account is no longer a CDN participant. + assert_eq!(DdcStaking::cdns(provider_stash), None); + + // Start unbonding all tokens + assert_ok!(DdcStaking::unbond(RuntimeOrigin::signed(provider_controller), cdn_bond_size)); + System::assert_has_event(Event::LeaveSoon(provider_stash).into()); + assert_eq!(DdcStaking::leaving_cdns(provider_stash), Some(cluster_id)); + System::assert_last_event(Event::Unbonded(provider_stash, cdn_bond_size).into()); + + let unbonding = System::block_number() + cdn_unbond_delay; + // Set the block number that allows us to chill. + while System::block_number() < unbonding { + System::set_block_number(System::block_number() + 1); + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + } + + assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(provider_controller))); + System::assert_has_event(Event::Withdrawn(provider_stash, cdn_bond_size).into()); + assert_eq!(DdcStaking::leaving_cdns(provider_stash), None); + System::assert_last_event(Event::Left(provider_stash).into()); + + MockNodeVisitor::reset_and_release_lock(lock); + }); +} + +#[test] +fn storage_full_unbonding_works() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + let provider_stash: u64 = 3; + let provider_controller: u64 = 4; + let cluster_id = ClusterId::from([1; 20]); + let node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])); + + let lock = MockNodeVisitor::set_and_hold_lock(MockNode { + cluster_id: Some(cluster_id), + exists: true, + }); + + let storage_bond_size = 10_u128; + let storage_chill_delay = 10_u64; + let storage_unbond_delay = 10_u64; + + // Put some money in account that we'll use. + let _ = Balances::make_free_balance_be(&provider_controller, 2000); + let _ = Balances::make_free_balance_be(&provider_stash, 2000); + + // Add new Storage participant, account 1 controlled by 2 with node 1. + assert_ok!(DdcStaking::bond( + RuntimeOrigin::signed(provider_stash), + provider_controller, + node_pub_key.clone(), + storage_bond_size, // min bond size + )); + System::assert_last_event(Event::Bonded(provider_stash, storage_bond_size).into()); + assert_ok!(DdcStaking::store(RuntimeOrigin::signed(provider_controller), cluster_id)); + System::assert_last_event(Event::Activated(provider_stash).into()); + + assert_eq!(DdcStaking::storages(provider_stash), Some(cluster_id)); + assert_eq!(DdcStaking::nodes(node_pub_key), Some(provider_stash)); + + // Set block timestamp. + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + + // Schedule Storage participant removal. + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller))); + let chilling = System::block_number() + storage_chill_delay; + System::assert_last_event(Event::ChillSoon(provider_stash, cluster_id, chilling).into()); + + // Set the block number that allows us to chill. + while System::block_number() < chilling { + System::set_block_number(System::block_number() + 1); + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + } + + // Actual Storage participant removal. + assert_ok!(DdcStaking::chill(RuntimeOrigin::signed(provider_controller))); + System::assert_last_event(Event::Chilled(provider_stash).into()); + + // Account is no longer a Storage participant. + assert_eq!(DdcStaking::storages(provider_stash), None); + + // Start unbonding all tokens + assert_ok!(DdcStaking::unbond( + RuntimeOrigin::signed(provider_controller), + storage_bond_size + )); + System::assert_has_event(Event::LeaveSoon(provider_stash).into()); + assert_eq!(DdcStaking::leaving_storages(provider_stash), Some(cluster_id)); + System::assert_last_event(Event::Unbonded(provider_stash, storage_bond_size).into()); + + let unbonding = System::block_number() + storage_unbond_delay; + // Set the block number that allows us to chill. + while System::block_number() < unbonding { + System::set_block_number(System::block_number() + 1); + Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); + } + + assert_ok!(DdcStaking::withdraw_unbonded(RuntimeOrigin::signed(provider_controller))); + System::assert_has_event(Event::Withdrawn(provider_stash, storage_bond_size).into()); + assert_eq!(DdcStaking::leaving_storages(provider_stash), None); + System::assert_last_event(Event::Left(provider_stash).into()); + + MockNodeVisitor::reset_and_release_lock(lock); + }); +} + +#[test] +fn staking_creator_works() { + // Verifies initial conditions of mock + ExtBuilder::default().build_and_execute(|| { + let stash: u64 = 1; + let controller: u64 = 2; + let cluster_id = ClusterId::from([1; 20]); + let value = 5; + let cdn_node_pub_key = NodePubKey::StoragePubKey(StorageNodePubKey::new([2; 32])); + let storage_node_pub_key = NodePubKey::CDNPubKey(CDNNodePubKey::new([2; 32])); + + assert_ok!( + >>::bond_stake_and_participate( + stash, + controller, + cdn_node_pub_key, + value, + cluster_id, + ) + ); + + assert_ok!( + >>::bond_stake_and_participate( + stash, + controller, + storage_node_pub_key, + value, + cluster_id, + ) + ); + }); +} + +#[test] +fn staking_visitor_works() { + // Verifies initial conditions of mock + ExtBuilder::default().build_and_execute(|| { + let cluster_id = ClusterId::from([1; 20]); + let node_pub_key = NodePubKey::CDNPubKey(CDNNodePubKey::new([5; 32])); + + // Add new CDN participant, account 3 controlled by 4 with node 5. + assert_ok!(DdcStaking::bond(RuntimeOrigin::signed(3), 4, node_pub_key.clone(), 100)); + + assert!(>::has_stake(&node_pub_key,)); + + if let Ok(result) = + >::has_chilling_attempt(&node_pub_key) + { + assert!(!result); + } + + assert_ok!(DdcStaking::serve(RuntimeOrigin::signed(4), ClusterId::from([1; 20]))); + + if let Ok(result) = + >::has_activated_stake(&node_pub_key, &cluster_id) + { + assert!(result); + } + }); +} diff --git a/pallets/ddc-staking/src/weights.rs b/pallets/ddc-staking/src/weights.rs index efca88632..5437d12a2 100644 --- a/pallets/ddc-staking/src/weights.rs +++ b/pallets/ddc-staking/src/weights.rs @@ -44,72 +44,95 @@ impl WeightInfo for SubstrateWeight { // Storage: DdcStaking Bonded (r:1 w:1) // Storage: DdcStaking Ledger (r:1 w:1) // Storage: DdcStaking Nodes (r:1 w:1) + // Storage: DdcStaking Providers (r:1 w:1) + // Storage: DdcNodes CDNNodes (r:1 w:0) // Storage: Balances Locks (r:1 w:1) fn bond() -> Weight { - Weight::from_ref_time(55_007_000_u64) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(37_000_000_u64) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } // Storage: DdcStaking Ledger (r:1 w:1) // Storage: DdcStaking CDNs (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking CurrentEra (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) + // Storage: DdcNodes CDNNodes (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn unbond() -> Weight { - Weight::from_ref_time(47_727_000_u64) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Minimum execution time: 37_000 nanoseconds. + Weight::from_ref_time(38_000_000_u64) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking CurrentEra (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) + // Storage: DdcStaking LeavingCDNs (r:1 w:0) + // Storage: DdcStaking LeavingStorages (r:1 w:0) fn withdraw_unbonded() -> Weight { - Weight::from_ref_time(69_750_000_u64) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Minimum execution time: 33_000 nanoseconds. + Weight::from_ref_time(34_000_000_u64) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + // Storage: DdcClusters Clusters (r:1 w:0) // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcStaking Settings (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) // Storage: DdcStaking CDNs (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:1) + // Storage: DdcStaking LeavingStorages (r:1 w:0) fn store() -> Weight { - Weight::from_ref_time(26_112_000_u64) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Minimum execution time: 28_000 nanoseconds. + Weight::from_ref_time(29_000_000_u64) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + // Storage: DdcClusters Clusters (r:1 w:0) // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcStaking Settings (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) // Storage: DdcStaking CDNs (r:1 w:1) + // Storage: DdcStaking LeavingCDNs (r:1 w:0) fn serve() -> Weight { - Weight::from_ref_time(19_892_000_u64) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Minimum execution time: 27_000 nanoseconds. + Weight::from_ref_time(28_000_000_u64) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking CurrentEra (r:1 w:0) // Storage: DdcStaking CDNs (r:1 w:1) - // Storage: DdcStaking Settings (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:0) fn chill() -> Weight { - Weight::from_ref_time(77_450_000_u64) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 27_000 nanoseconds. + Weight::from_ref_time(28_000_000_u64) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } // Storage: DdcStaking Bonded (r:1 w:1) // Storage: DdcStaking Ledger (r:2 w:2) fn set_controller() -> Weight { - Weight::from_ref_time(38_521_000_u64) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_000_000_u64) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: DdcStaking Nodes (r:1 w:1) + // Storage: DdcStaking Nodes (r:1 w:2) + // Storage: DdcStaking Providers (r:1 w:1) + // Storage: DdcStaking CDNs (r:1 w:0) + // Storage: DdcStaking Storages (r:1 w:0) + // Storage: DdcStaking LeavingCDNs (r:1 w:0) + // Storage: DdcStaking LeavingStorages (r:1 w:0) fn set_node() -> Weight { - Weight::from_ref_time(21_779_000_u64) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_000_000_u64) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } @@ -118,71 +141,94 @@ impl WeightInfo for () { // Storage: DdcStaking Bonded (r:1 w:1) // Storage: DdcStaking Ledger (r:1 w:1) // Storage: DdcStaking Nodes (r:1 w:1) + // Storage: DdcStaking Providers (r:1 w:1) + // Storage: DdcNodes CDNNodes (r:1 w:0) // Storage: Balances Locks (r:1 w:1) fn bond() -> Weight { - Weight::from_ref_time(55_007_000_u64) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(37_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } // Storage: DdcStaking Ledger (r:1 w:1) // Storage: DdcStaking CDNs (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:0) - // Storage: DdcStaking CurrentEra (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) + // Storage: DdcNodes CDNNodes (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) fn unbond() -> Weight { - Weight::from_ref_time(47_727_000_u64) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Minimum execution time: 37_000 nanoseconds. + Weight::from_ref_time(38_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking CurrentEra (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) + // Storage: DdcStaking LeavingCDNs (r:1 w:0) + // Storage: DdcStaking LeavingStorages (r:1 w:0) fn withdraw_unbonded() -> Weight { - Weight::from_ref_time(69_750_000_u64) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Minimum execution time: 33_000 nanoseconds. + Weight::from_ref_time(34_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + // Storage: DdcClusters Clusters (r:1 w:0) // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcStaking Settings (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) // Storage: DdcStaking CDNs (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:1) + // Storage: DdcStaking LeavingStorages (r:1 w:0) fn store() -> Weight { - Weight::from_ref_time(26_112_000_u64) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Minimum execution time: 28_000 nanoseconds. + Weight::from_ref_time(29_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + // Storage: DdcClusters Clusters (r:1 w:0) // Storage: DdcStaking Ledger (r:1 w:0) - // Storage: DdcStaking Settings (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:0) + // Storage: DdcStaking Providers (r:1 w:0) // Storage: DdcStaking CDNs (r:1 w:1) + // Storage: DdcStaking LeavingCDNs (r:1 w:0) fn serve() -> Weight { - Weight::from_ref_time(19_892_000_u64) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Minimum execution time: 27_000 nanoseconds. + Weight::from_ref_time(28_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } // Storage: DdcStaking Ledger (r:1 w:1) - // Storage: DdcStaking CurrentEra (r:1 w:0) // Storage: DdcStaking CDNs (r:1 w:1) - // Storage: DdcStaking Settings (r:1 w:0) + // Storage: DdcClusters ClustersGovParams (r:1 w:0) // Storage: DdcStaking Storages (r:1 w:0) fn chill() -> Weight { - Weight::from_ref_time(77_450_000_u64) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Minimum execution time: 27_000 nanoseconds. + Weight::from_ref_time(28_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } // Storage: DdcStaking Bonded (r:1 w:1) // Storage: DdcStaking Ledger (r:2 w:2) fn set_controller() -> Weight { - Weight::from_ref_time(38_521_000_u64) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_000_000_u64) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: DdcStaking Nodes (r:1 w:1) + // Storage: DdcStaking Nodes (r:1 w:2) + // Storage: DdcStaking Providers (r:1 w:1) + // Storage: DdcStaking CDNs (r:1 w:0) + // Storage: DdcStaking Storages (r:1 w:0) + // Storage: DdcStaking LeavingCDNs (r:1 w:0) + // Storage: DdcStaking LeavingStorages (r:1 w:0) fn set_node() -> Weight { - Weight::from_ref_time(21_779_000_u64) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_000_000_u64) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 54b1c0c9e..456c33299 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,16 +1,44 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; -use scale_info::TypeInfo; +use scale_info::{prelude::vec::Vec, TypeInfo}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::hash::H160; use sp_runtime::{AccountId32, Perquintill, RuntimeDebug}; + pub type ClusterId = H160; pub type DdcEra = u32; pub type BucketId = u64; pub type StorageNodePubKey = AccountId32; pub type CDNNodePubKey = AccountId32; + +// ClusterParams includes Governance non-sensetive parameters only +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub struct ClusterParams { + pub node_provider_auth_contract: Option, +} + +// ClusterGovParams includes Governance sensitive parameters +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Default)] +#[scale_info(skip_type_params(Balance, BlockNumber, T))] +pub struct ClusterGovParams { + pub treasury_share: Perquintill, + pub validators_share: Perquintill, + pub cluster_reserve_share: Perquintill, + pub cdn_bond_size: Balance, + pub cdn_chill_delay: BlockNumber, + pub cdn_unbonding_delay: BlockNumber, + pub storage_bond_size: Balance, + pub storage_chill_delay: BlockNumber, + pub storage_unbonding_delay: BlockNumber, + pub unit_per_mb_stored: u128, + pub unit_per_mb_streamed: u128, + pub unit_per_put_request: u128, + pub unit_per_get_request: u128, +} + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub struct ClusterPricingParams { pub unit_per_mb_stored: u128, @@ -26,6 +54,16 @@ pub struct ClusterFeesParams { pub cluster_reserve_share: Perquintill, } +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub struct ClusterBondingParams { + pub cdn_bond_size: u128, + pub cdn_chill_delay: BlockNumber, + pub cdn_unbonding_delay: BlockNumber, + pub storage_bond_size: u128, + pub storage_chill_delay: BlockNumber, + pub storage_unbonding_delay: BlockNumber, +} + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub enum NodePubKey { @@ -58,3 +96,26 @@ impl TryFrom for NodeType { } } } + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub struct CDNNodeParams { + pub host: Vec, + pub http_port: u16, + pub grpc_port: u16, + pub p2p_port: u16, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub struct StorageNodeParams { + pub host: Vec, + pub http_port: u16, + pub grpc_port: u16, + pub p2p_port: u16, +} + +// Params fields are always coming from extrinsic input +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] +pub enum NodeParams { + StorageParams(StorageNodeParams), + CDNParams(CDNNodeParams), +} diff --git a/runtime/cere-dev/Cargo.toml b/runtime/cere-dev/Cargo.toml index e0e425c13..87821e239 100644 --- a/runtime/cere-dev/Cargo.toml +++ b/runtime/cere-dev/Cargo.toml @@ -217,7 +217,11 @@ runtime-benchmarks = [ "pallet-session-benchmarking/runtime-benchmarks", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", + "pallet-ddc-customers/runtime-benchmarks", + "pallet-ddc-clusters/runtime-benchmarks", + "pallet-ddc-nodes/runtime-benchmarks", "pallet-ddc-staking/runtime-benchmarks", + "pallet-ddc-payouts/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-tips/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", diff --git a/runtime/cere-dev/src/lib.rs b/runtime/cere-dev/src/lib.rs index 5aa907989..9b70d24a7 100644 --- a/runtime/cere-dev/src/lib.rs +++ b/runtime/cere-dev/src/lib.rs @@ -1322,7 +1322,10 @@ impl pallet_ddc_staking::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_ddc_staking::weights::SubstrateWeight; type ClusterVisitor = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type ClusterManager = pallet_ddc_clusters::Pallet; type NodeVisitor = pallet_ddc_nodes::Pallet; + type NodeCreator = pallet_ddc_nodes::Pallet; } parameter_types! { @@ -1336,17 +1339,23 @@ impl pallet_ddc_customers::Config for Runtime { type PalletId = DdcCustomersPalletId; type RuntimeEvent = RuntimeEvent; type ClusterVisitor = pallet_ddc_clusters::Pallet; + type ClusterCreator = pallet_ddc_clusters::Pallet; + type WeightInfo = pallet_ddc_customers::weights::SubstrateWeight; } impl pallet_ddc_nodes::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type StakingVisitor = pallet_ddc_staking::Pallet; + type WeightInfo = pallet_ddc_nodes::weights::SubstrateWeight; } impl pallet_ddc_clusters::Config for Runtime { type RuntimeEvent = RuntimeEvent; type NodeRepository = pallet_ddc_nodes::Pallet; type StakingVisitor = pallet_ddc_staking::Pallet; + type StakerCreator = pallet_ddc_staking::Pallet; type Currency = Balances; + type WeightInfo = pallet_ddc_clusters::weights::SubstrateWeight; } parameter_types! { @@ -1365,9 +1374,12 @@ impl pallet_ddc_payouts::Config for Runtime { type PalletId = PayoutsPalletId; type Currency = Balances; type CustomerCharger = DdcCustomers; + type CustomerDepositor = DdcCustomers; type ClusterVisitor = DdcClusters; type TreasuryVisitor = TreasureWrapper; type ValidatorList = pallet_staking::UseValidatorsMap; + type ClusterCreator = DdcClusters; + type WeightInfo = pallet_ddc_payouts::weights::SubstrateWeight; } construct_runtime!( @@ -1533,7 +1545,11 @@ mod benches { [pallet_scheduler, Scheduler] [pallet_session, SessionBench::] [pallet_staking, Staking] + [pallet_ddc_customers, DdcCustomers] + [pallet_ddc_clusters, DdcClusters] [pallet_ddc_staking, DdcStaking] + [pallet_ddc_nodes, DdcNodes] + [pallet_ddc_payouts, DdcPayouts] [frame_system, SystemBench::] [pallet_timestamp, Timestamp] [pallet_tips, Tips] diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 79d2bcad3..9f1e81c34 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } ddc-primitives = { path = "../primitives", default-features = false } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30" } frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", default-features = false } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", default-features = false } diff --git a/traits/src/cluster.rs b/traits/src/cluster.rs index dcd1f259d..d980c1ae4 100644 --- a/traits/src/cluster.rs +++ b/traits/src/cluster.rs @@ -1,12 +1,14 @@ use codec::{Decode, Encode}; -use ddc_primitives::{ClusterFeesParams, ClusterId, ClusterPricingParams, NodePubKey, NodeType}; +use ddc_primitives::{ + ClusterBondingParams, ClusterFeesParams, ClusterGovParams, ClusterId, ClusterParams, + ClusterPricingParams, NodePubKey, NodeType, +}; +use frame_support::dispatch::DispatchResult; use frame_system::Config; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; pub trait ClusterVisitor { - fn cluster_has_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool; - fn ensure_cluster(cluster_id: &ClusterId) -> Result<(), ClusterVisitorError>; fn get_bond_size( @@ -31,6 +33,20 @@ pub trait ClusterVisitor { cluster_id: &ClusterId, node_type: NodeType, ) -> Result; + + fn get_bonding_params( + cluster_id: &ClusterId, + ) -> Result, ClusterVisitorError>; +} + +pub trait ClusterCreator { + fn create_new_cluster( + cluster_id: ClusterId, + cluster_manager_id: T::AccountId, + cluster_reserve_id: T::AccountId, + cluster_params: ClusterParams, + cluster_gov_params: ClusterGovParams, + ) -> DispatchResult; } #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] @@ -38,3 +54,22 @@ pub enum ClusterVisitorError { ClusterDoesNotExist, ClusterGovParamsNotSet, } + +pub trait ClusterManager { + fn contains_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool; + fn add_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError>; + fn remove_node( + cluster_id: &ClusterId, + node_pub_key: &NodePubKey, + ) -> Result<(), ClusterManagerError>; +} + +pub enum ClusterManagerError { + AttemptToAddNonExistentNode, + AttemptToAddAlreadyAssignedNode, + AttemptToRemoveNotAssignedNode, + AttemptToRemoveNonExistentNode, +} diff --git a/traits/src/customer.rs b/traits/src/customer.rs index 2fd624af1..746a20f6d 100644 --- a/traits/src/customer.rs +++ b/traits/src/customer.rs @@ -1,6 +1,5 @@ -use codec::{Decode, Encode}; -use scale_info::TypeInfo; -use sp_runtime::{DispatchError, RuntimeDebug}; +use core::u128; +use sp_runtime::DispatchError; pub trait CustomerCharger { fn charge_content_owner( @@ -10,10 +9,7 @@ pub trait CustomerCharger { ) -> Result; } -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] -pub enum CustomerChargerError { - NotOwner, - ArithmeticUnderflow, - TransferFailed, - UnlockFailed, +pub trait CustomerDepositor { + fn deposit(customer: T::AccountId, amount: u128) -> Result<(), DispatchError>; + fn deposit_extra(customer: T::AccountId, amount: u128) -> Result<(), DispatchError>; } diff --git a/traits/src/node.rs b/traits/src/node.rs index af22b5a8b..ed60e0f1a 100644 --- a/traits/src/node.rs +++ b/traits/src/node.rs @@ -1,8 +1,18 @@ -use ddc_primitives::{ClusterId, NodePubKey}; +use ddc_primitives::{ClusterId, NodeParams, NodePubKey}; +use frame_support::dispatch::DispatchResult; use frame_system::Config; pub trait NodeVisitor { fn get_cluster_id(node_pub_key: &NodePubKey) -> Result, NodeVisitorError>; + fn exists(node_pub_key: &NodePubKey) -> bool; +} + +pub trait NodeCreator { + fn create_node( + node_pub_key: NodePubKey, + provider_id: T::AccountId, + node_params: NodeParams, + ) -> DispatchResult; } pub enum NodeVisitorError { diff --git a/traits/src/staking.rs b/traits/src/staking.rs index 5ec1e8bcf..06697856f 100644 --- a/traits/src/staking.rs +++ b/traits/src/staking.rs @@ -2,12 +2,24 @@ use ddc_primitives::{ClusterId, NodePubKey}; use frame_system::Config; pub trait StakingVisitor { - fn node_has_stake( + fn has_activated_stake( node_pub_key: &NodePubKey, cluster_id: &ClusterId, ) -> Result; - fn node_is_chilling(node_pub_key: &NodePubKey) -> Result; + fn has_stake(node_pub_key: &NodePubKey) -> bool; + + fn has_chilling_attempt(node_pub_key: &NodePubKey) -> Result; +} + +pub trait StakerCreator { + fn bond_stake_and_participate( + stash: T::AccountId, + controller: T::AccountId, + node: NodePubKey, + value: Balance, + cluster_id: ClusterId, + ) -> sp_runtime::DispatchResult; } pub enum StakingVisitorError {