diff --git a/Cargo.lock b/Cargo.lock index 48673e57ce542..25bb3afcfc19c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1538,9 +1538,9 @@ dependencies = [ [[package]] name = "environmental" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6576a1755ddffd988788025e75bce9e74b018f7cc226198fe931d077911c6d7e" +checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" [[package]] name = "erased-serde" @@ -2630,6 +2630,22 @@ dependencies = [ "libc", ] +[[package]] +name = "if-watch" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b8538953a3f0d0d3868f0a706eb4273535e10d72acb5c82c1c23ae48835c85" +dependencies = [ + "async-io", + "futures 0.3.13", + "futures-lite", + "if-addrs", + "ipnet", + "libc", + "log", + "winapi 0.3.9", +] + [[package]] name = "if-watch" version = "0.2.0" @@ -2917,9 +2933,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.2.0-alpha" +version = "0.2.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "124797a4ea7430d0675db78e065e53316e3f1a3cbf0ee4d6dbdd42db7b08e193" +checksum = "7f1d8440e2617bdebdf45114e90f65aed3f14bf73e23d874dde8e4b764676fe9" dependencies = [ "async-trait", "futures 0.3.13", @@ -2963,9 +2979,9 @@ dependencies = [ [[package]] name = "jsonrpsee-utils" -version = "0.2.0-alpha" +version = "0.2.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e45394ec3175a767c3c5bac584560e6ad9b56ebd73216c85ec8bab49619244" +checksum = "e51670a3b56e5fb0d325920ce317c76184b8afabfd7bc5009831229cfef0732b" dependencies = [ "futures 0.3.13", "globset", @@ -3109,6 +3125,44 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" +[[package]] +name = "libp2p" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc225a49973cf9ab10d0cdd6a4b8f0cda299df9b760824bbb623f15f8f0c95a" +dependencies = [ + "atomic", + "bytes 1.0.1", + "futures 0.3.13", + "lazy_static", + "libp2p-core 0.27.1", + "libp2p-deflate 0.27.1", + "libp2p-dns 0.27.0", + "libp2p-floodsub 0.27.0", + "libp2p-gossipsub 0.28.0", + "libp2p-identify 0.27.0", + "libp2p-kad 0.28.1", + "libp2p-mdns 0.28.1", + "libp2p-mplex 0.27.1", + "libp2p-noise 0.29.0", + "libp2p-ping 0.27.0", + "libp2p-plaintext 0.27.1", + "libp2p-pnet", + "libp2p-request-response 0.9.1", + "libp2p-swarm 0.27.2", + "libp2p-swarm-derive", + "libp2p-tcp 0.27.1", + "libp2p-uds 0.27.0", + "libp2p-wasm-ext 0.27.0", + "libp2p-websocket 0.28.0", + "libp2p-yamux 0.30.1", + "parity-multiaddr", + "parking_lot 0.11.1", + "pin-project 1.0.5", + "smallvec 1.6.1", + "wasm-timer", +] + [[package]] name = "libp2p" version = "0.36.0" @@ -3119,28 +3173,28 @@ dependencies = [ "bytes 1.0.1", "futures 0.3.13", "lazy_static", - "libp2p-core", - "libp2p-deflate", - "libp2p-dns", - "libp2p-floodsub", - "libp2p-gossipsub", - "libp2p-identify", - "libp2p-kad", - "libp2p-mdns", - "libp2p-mplex", - "libp2p-noise", - "libp2p-ping", - "libp2p-plaintext", + "libp2p-core 0.28.1", + "libp2p-deflate 0.28.0", + "libp2p-dns 0.28.0", + "libp2p-floodsub 0.28.0", + "libp2p-gossipsub 0.29.0", + "libp2p-identify 0.28.0", + "libp2p-kad 0.29.0", + "libp2p-mdns 0.29.0", + "libp2p-mplex 0.28.0", + "libp2p-noise 0.30.0", + "libp2p-ping 0.28.0", + "libp2p-plaintext 0.28.0", "libp2p-pnet", "libp2p-relay", - "libp2p-request-response", - "libp2p-swarm", + "libp2p-request-response 0.10.0", + "libp2p-swarm 0.28.0", "libp2p-swarm-derive", - "libp2p-tcp", - "libp2p-uds", - "libp2p-wasm-ext", - "libp2p-websocket", - "libp2p-yamux", + "libp2p-tcp 0.28.0", + "libp2p-uds 0.28.0", + "libp2p-wasm-ext 0.28.0", + "libp2p-websocket 0.29.0", + "libp2p-yamux 0.31.0", "parity-multiaddr", "parking_lot 0.11.1", "pin-project 1.0.5", @@ -3148,6 +3202,40 @@ dependencies = [ "wasm-timer", ] +[[package]] +name = "libp2p-core" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2d56aadc2c2bf22cd7797f86e56a65b5b3994a0136b65be3106938acae7a26" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv", + "futures 0.3.13", + "futures-timer 3.0.2", + "lazy_static", + "libsecp256k1", + "log", + "multihash", + "multistream-select", + "parity-multiaddr", + "parking_lot 0.11.1", + "pin-project 1.0.5", + "prost", + "prost-build", + "rand 0.7.3", + "ring", + "rw-stream-sink", + "sha2 0.9.3", + "smallvec 1.6.1", + "thiserror", + "unsigned-varint 0.7.0", + "void", + "zeroize", +] + [[package]] name = "libp2p-core" version = "0.28.1" @@ -3182,6 +3270,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "libp2p-deflate" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d42eed63305f0420736fa487f9acef720c4528bd7852a6a760f5ccde4813345" +dependencies = [ + "flate2", + "futures 0.3.13", + "libp2p-core 0.27.1", +] + [[package]] name = "libp2p-deflate" version = "0.28.0" @@ -3190,7 +3289,18 @@ checksum = "a2181a641cd15f9b6ba71b1335800f309012a0a97a29ffaabbbf40e9d3d58f08" dependencies = [ "flate2", "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", +] + +[[package]] +name = "libp2p-dns" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5153b6db68fd4baa3b304e377db744dd8fea8ff4e4504509ee636abcde88d3e3" +dependencies = [ + "futures 0.3.13", + "libp2p-core 0.27.1", + "log", ] [[package]] @@ -3201,12 +3311,30 @@ checksum = "9712eb3e9f7dcc77cc5ca7d943b6a85ce4b1faaf91a67e003442412a26d6d6f8" dependencies = [ "async-std-resolver", "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", "log", "smallvec 1.6.1", "trust-dns-resolver", ] +[[package]] +name = "libp2p-floodsub" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c63dfa06581b24b1d12bf9815b43689a784424be217d6545c800c7c75a207f" +dependencies = [ + "cuckoofilter", + "fnv", + "futures 0.3.13", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", + "log", + "prost", + "prost-build", + "rand 0.7.3", + "smallvec 1.6.1", +] + [[package]] name = "libp2p-floodsub" version = "0.28.0" @@ -3216,13 +3344,39 @@ dependencies = [ "cuckoofilter", "fnv", "futures 0.3.13", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", + "log", + "prost", + "prost-build", + "rand 0.7.3", + "smallvec 1.6.1", +] + +[[package]] +name = "libp2p-gossipsub" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502dc5fcbfec4aa1c63ef3f7307ffe20e90c1a1387bf23ed0bec087f2dde58a1" +dependencies = [ + "asynchronous-codec 0.6.0", + "base64 0.13.0", + "byteorder", + "bytes 1.0.1", + "fnv", + "futures 0.3.13", + "hex_fmt", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", "log", "prost", "prost-build", "rand 0.7.3", + "regex", + "sha2 0.9.3", "smallvec 1.6.1", + "unsigned-varint 0.7.0", + "wasm-timer", ] [[package]] @@ -3238,8 +3392,8 @@ dependencies = [ "fnv", "futures 0.3.13", "hex_fmt", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", "log", "prost", "prost-build", @@ -3251,6 +3405,22 @@ dependencies = [ "wasm-timer", ] +[[package]] +name = "libp2p-identify" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40fb36a059b7a8cce1514bd8b546fa612e006c9937caa7f5950cb20021fe91e" +dependencies = [ + "futures 0.3.13", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", + "log", + "prost", + "prost-build", + "smallvec 1.6.1", + "wasm-timer", +] + [[package]] name = "libp2p-identify" version = "0.28.0" @@ -3258,12 +3428,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f88ebc841d744979176ab4b8b294a3e655a7ba4ef26a905d073a52b49ed4dff5" dependencies = [ "futures 0.3.13", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", + "log", + "prost", + "prost-build", + "smallvec 1.6.1", + "wasm-timer", +] + +[[package]] +name = "libp2p-kad" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3da6c9acbcc05f93235d201d7d45ef4e8b88a45d8836f98becd8b4d443f066" +dependencies = [ + "arrayvec 0.5.2", + "asynchronous-codec 0.6.0", + "bytes 1.0.1", + "either", + "fnv", + "futures 0.3.13", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", "log", "prost", "prost-build", + "rand 0.7.3", + "sha2 0.9.3", "smallvec 1.6.1", + "uint", + "unsigned-varint 0.7.0", + "void", "wasm-timer", ] @@ -3279,8 +3475,8 @@ dependencies = [ "either", "fnv", "futures 0.3.13", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", "log", "prost", "prost-build", @@ -3293,6 +3489,27 @@ dependencies = [ "wasm-timer", ] +[[package]] +name = "libp2p-mdns" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9e6374814d1b118d97ccabdfc975c8910bd16dc38a8bc058eeb08bf2080fe1" +dependencies = [ + "async-io", + "data-encoding", + "dns-parser", + "futures 0.3.13", + "if-watch 0.1.8", + "lazy_static", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", + "log", + "rand 0.7.3", + "smallvec 1.6.1", + "socket2 0.3.19", + "void", +] + [[package]] name = "libp2p-mdns" version = "0.29.0" @@ -3303,10 +3520,10 @@ dependencies = [ "data-encoding", "dns-parser", "futures 0.3.13", - "if-watch", + "if-watch 0.2.0", "lazy_static", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", "log", "rand 0.8.3", "smallvec 1.6.1", @@ -3314,6 +3531,24 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-mplex" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350ce8b3923594aedabd5d6e3f875d058435052a29c3f32df378bc70d10be464" +dependencies = [ + "asynchronous-codec 0.6.0", + "bytes 1.0.1", + "futures 0.3.13", + "libp2p-core 0.27.1", + "log", + "nohash-hasher", + "parking_lot 0.11.1", + "rand 0.7.3", + "smallvec 1.6.1", + "unsigned-varint 0.7.0", +] + [[package]] name = "libp2p-mplex" version = "0.28.0" @@ -3323,7 +3558,7 @@ dependencies = [ "asynchronous-codec 0.6.0", "bytes 1.0.1", "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", "log", "nohash-hasher", "parking_lot 0.11.1", @@ -3332,6 +3567,28 @@ dependencies = [ "unsigned-varint 0.7.0", ] +[[package]] +name = "libp2p-noise" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aca322b52a0c5136142a7c3971446fb1e9964923a526c9cc6ef3b7c94e57778" +dependencies = [ + "bytes 1.0.1", + "curve25519-dalek 3.0.2", + "futures 0.3.13", + "lazy_static", + "libp2p-core 0.27.1", + "log", + "prost", + "prost-build", + "rand 0.7.3", + "sha2 0.9.3", + "snow", + "static_assertions", + "x25519-dalek", + "zeroize", +] + [[package]] name = "libp2p-noise" version = "0.30.0" @@ -3342,7 +3599,7 @@ dependencies = [ "curve25519-dalek 3.0.2", "futures 0.3.13", "lazy_static", - "libp2p-core", + "libp2p-core 0.28.1", "log", "prost", "prost-build", @@ -3354,6 +3611,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "libp2p-ping" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3813276d0708c8db0f500d8beda1bda9ad955723b9cb272c41f4727256f73c" +dependencies = [ + "futures 0.3.13", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", + "log", + "rand 0.7.3", + "void", + "wasm-timer", +] + [[package]] name = "libp2p-ping" version = "0.28.0" @@ -3361,14 +3633,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea10fc5209260915ea65b78f612d7ff78a29ab288e7aa3250796866af861c45" dependencies = [ "futures 0.3.13", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", "log", "rand 0.7.3", "void", "wasm-timer", ] +[[package]] +name = "libp2p-plaintext" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d58defcadb646ae4b033e130b48d87410bf76394dc3335496cae99dac803e61" +dependencies = [ + "asynchronous-codec 0.6.0", + "bytes 1.0.1", + "futures 0.3.13", + "libp2p-core 0.27.1", + "log", + "prost", + "prost-build", + "unsigned-varint 0.7.0", + "void", +] + [[package]] name = "libp2p-plaintext" version = "0.28.0" @@ -3378,7 +3667,7 @@ dependencies = [ "asynchronous-codec 0.6.0", "bytes 1.0.1", "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", "log", "prost", "prost-build", @@ -3410,8 +3699,8 @@ dependencies = [ "bytes 1.0.1", "futures 0.3.13", "futures-timer 3.0.2", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", "log", "pin-project 1.0.5", "prost", @@ -3423,6 +3712,26 @@ dependencies = [ "wasm-timer", ] +[[package]] +name = "libp2p-request-response" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10e5552827c33d8326502682da73a0ba4bfa40c1b55b216af3c303f32169dd89" +dependencies = [ + "async-trait", + "bytes 1.0.1", + "futures 0.3.13", + "libp2p-core 0.27.1", + "libp2p-swarm 0.27.2", + "log", + "lru", + "minicbor 0.7.2", + "rand 0.7.3", + "smallvec 1.6.1", + "unsigned-varint 0.7.0", + "wasm-timer", +] + [[package]] name = "libp2p-request-response" version = "0.10.0" @@ -3432,17 +3741,33 @@ dependencies = [ "async-trait", "bytes 1.0.1", "futures 0.3.13", - "libp2p-core", - "libp2p-swarm", + "libp2p-core 0.28.1", + "libp2p-swarm 0.28.0", "log", "lru", - "minicbor", + "minicbor 0.8.0", "rand 0.7.3", "smallvec 1.6.1", "unsigned-varint 0.7.0", "wasm-timer", ] +[[package]] +name = "libp2p-swarm" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7955b973e1fd2bd61ffd43ce261c1223f61f4aacd5bae362a924993f9a25fd98" +dependencies = [ + "either", + "futures 0.3.13", + "libp2p-core 0.27.1", + "log", + "rand 0.7.3", + "smallvec 1.6.1", + "void", + "wasm-timer", +] + [[package]] name = "libp2p-swarm" version = "0.28.0" @@ -3451,7 +3776,7 @@ checksum = "75c26980cadd7c25d89071cb23e1f7f5df4863128cc91d83c6ddc72338cecafa" dependencies = [ "either", "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", "log", "rand 0.7.3", "smallvec 1.6.1", @@ -3469,6 +3794,23 @@ dependencies = [ "syn", ] +[[package]] +name = "libp2p-tcp" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a5aef80e519a6cb8e2663605142f97baaaea1a252eecbf8756184765f7471b" +dependencies = [ + "async-io", + "futures 0.3.13", + "futures-timer 3.0.2", + "if-watch 0.1.8", + "ipnet", + "libc", + "libp2p-core 0.27.1", + "log", + "socket2 0.3.19", +] + [[package]] name = "libp2p-tcp" version = "0.28.0" @@ -3478,14 +3820,26 @@ dependencies = [ "async-io", "futures 0.3.13", "futures-timer 3.0.2", - "if-watch", + "if-watch 0.2.0", "ipnet", "libc", - "libp2p-core", + "libp2p-core 0.28.1", "log", "socket2 0.4.0", ] +[[package]] +name = "libp2p-uds" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80ac51ce419f60be966e02103c17f67ff5dc4422ba83ba54d251d6c62a4ed487" +dependencies = [ + "async-std", + "futures 0.3.13", + "libp2p-core 0.27.1", + "log", +] + [[package]] name = "libp2p-uds" version = "0.28.0" @@ -3494,10 +3848,24 @@ checksum = "ffd6564bb3b7ff203661ccbb69003c2b551e34cef974f2d6c6a28306a12170b5" dependencies = [ "async-std", "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", "log", ] +[[package]] +name = "libp2p-wasm-ext" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6149c46cb76935c80bc8be6ec6e3ebd5f5e1679765a255fb34331d54610f15dd" +dependencies = [ + "futures 0.3.13", + "js-sys", + "libp2p-core 0.27.1", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "libp2p-wasm-ext" version = "0.28.0" @@ -3506,12 +3874,30 @@ checksum = "6df65fc13f6188edf7e6927b086330448b3ca27af86b49748c6d299d7c8d9040" dependencies = [ "futures 0.3.13", "js-sys", - "libp2p-core", + "libp2p-core 0.28.1", "parity-send-wrapper", "wasm-bindgen", "wasm-bindgen-futures", ] +[[package]] +name = "libp2p-websocket" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b1c6a3431045da8b925ed83384e4c5163e14b990572307fca9c507435d4d22" +dependencies = [ + "either", + "futures 0.3.13", + "futures-rustls", + "libp2p-core 0.27.1", + "log", + "quicksink", + "rw-stream-sink", + "soketto", + "url 2.2.1", + "webpki-roots", +] + [[package]] name = "libp2p-websocket" version = "0.29.0" @@ -3521,7 +3907,7 @@ dependencies = [ "either", "futures 0.3.13", "futures-rustls", - "libp2p-core", + "libp2p-core 0.28.1", "log", "quicksink", "rw-stream-sink", @@ -3530,6 +3916,19 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "libp2p-yamux" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4819358c542a86ff95f6ae691efb4b94ddaf477079b01a686f5705b79bfc232a" +dependencies = [ + "futures 0.3.13", + "libp2p-core 0.27.1", + "parking_lot 0.11.1", + "thiserror", + "yamux", +] + [[package]] name = "libp2p-yamux" version = "0.31.0" @@ -3537,7 +3936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d6144cc94143fb0a8dd1e7c2fbcc32a2808168bcd1d69920635424d5993b7b" dependencies = [ "futures 0.3.13", - "libp2p-core", + "libp2p-core 0.28.1", "parking_lot 0.11.1", "thiserror", "yamux", @@ -3794,6 +4193,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "minicbor" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c2b2c73f9640fccab53947e2b3474d5071fcbc8f82cac51ddf6c8041a30a9ea" +dependencies = [ + "minicbor-derive", +] + [[package]] name = "minicbor" version = "0.8.0" @@ -4073,7 +4481,7 @@ dependencies = [ "futures 0.3.13", "futures-timer 3.0.2", "jsonrpc-core", - "libp2p", + "libp2p 0.36.0", "node-cli", "sc-rpc-api", "serde", @@ -4094,7 +4502,7 @@ dependencies = [ "frame-system", "futures 0.3.13", "hex-literal", - "libp2p-wasm-ext", + "libp2p-wasm-ext 0.28.0", "log", "nix", "node-executor", @@ -4278,6 +4686,7 @@ name = "node-runtime" version = "2.0.1" dependencies = [ "frame-benchmarking", + "frame-election-provider-support", "frame-executive", "frame-support", "frame-system", @@ -5361,6 +5770,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "lazy_static", + "log", "pallet-timestamp", "parity-scale-codec 2.0.1", "serde", @@ -6898,7 +7308,7 @@ dependencies = [ "either", "futures 0.3.13", "futures-timer 3.0.2", - "libp2p", + "libp2p 0.36.0", "log", "parity-scale-codec 2.0.1", "prost", @@ -7000,7 +7410,7 @@ dependencies = [ "fdlimit", "futures 0.3.13", "hex", - "libp2p", + "libp2p 0.36.0", "log", "names", "parity-scale-codec 2.0.1", @@ -7615,7 +8025,7 @@ dependencies = [ "futures-timer 3.0.2", "hex", "ip_network", - "libp2p", + "libp2p 0.36.0", "linked-hash-map", "linked_hash_set", "log", @@ -7661,7 +8071,7 @@ dependencies = [ "async-std", "futures 0.3.13", "futures-timer 3.0.2", - "libp2p", + "libp2p 0.36.0", "log", "lru", "quickcheck", @@ -7681,7 +8091,7 @@ dependencies = [ "async-std", "futures 0.3.13", "futures-timer 3.0.2", - "libp2p", + "libp2p 0.36.0", "log", "parking_lot 0.11.1", "rand 0.7.3", @@ -7742,7 +8152,7 @@ name = "sc-peerset" version = "3.0.0" dependencies = [ "futures 0.3.13", - "libp2p", + "libp2p 0.36.0", "log", "rand 0.7.3", "serde_json", @@ -7998,7 +8408,7 @@ version = "3.0.0" dependencies = [ "chrono", "futures 0.3.13", - "libp2p", + "libp2p 0.36.0", "log", "parking_lot 0.11.1", "pin-project 1.0.5", @@ -8666,7 +9076,7 @@ version = "0.9.0" dependencies = [ "futures 0.3.13", "futures-timer 3.0.2", - "libp2p", + "libp2p 0.36.0", "log", "parity-scale-codec 2.0.1", "parking_lot 0.11.1", @@ -9394,7 +9804,7 @@ dependencies = [ "getrandom 0.2.2", "js-sys", "kvdb-web", - "libp2p-wasm-ext", + "libp2p-wasm-ext 0.28.0", "log", "rand 0.7.3", "sc-chain-spec", @@ -9712,7 +10122,7 @@ dependencies = [ "futures 0.1.31", "futures 0.3.13", "jsonrpc-core", - "libp2p", + "libp2p 0.35.1", "log", "node-cli", "parity-scale-codec 1.3.7", diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index 554e6c4af428d..d21aedd1d1849 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -139,7 +139,6 @@ fn construct_block( (Block { header, extrinsics }.encode(), hash.into()) } - fn test_blocks(genesis_config: &GenesisConfig, executor: &NativeExecutor) -> Vec<(Vec, Hash)> { @@ -147,7 +146,7 @@ fn test_blocks(genesis_config: &GenesisConfig, executor: &NativeExecutor (true, WasmExecutionMethod::Interpreted), - ExecutionMethod::Wasm(wasm_method) => (false, *wasm_method), - }; - - let executor = NativeExecutor::new(wasm_method, None, 8); - let runtime_code = RuntimeCode { - code_fetcher: &sp_core::traits::WrappedRuntimeCode(compact_code_unwrap().into()), - hash: vec![1, 2, 3], - heap_pages: None, - }; - - // Get the runtime version to initialize the runtimes cache. - { - let mut test_ext = new_test_ext(&genesis_config); - executor.runtime_version(&mut test_ext.ext(), &runtime_code).unwrap(); - } - - let blocks = test_blocks(&genesis_config, &executor); - - b.iter_batched_ref( - || new_test_ext(&genesis_config), - |test_ext| { - for block in blocks.iter() { - executor.call:: _>( - &mut test_ext.ext(), - &runtime_code, - "Core_execute_block", - &block.0, - use_native, - None, - ).0.unwrap(); - } - }, - BatchSize::LargeInput, - ); - }, - vec![ - ExecutionMethod::Native, - ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), - #[cfg(feature = "wasmtime")] - ExecutionMethod::Wasm(WasmExecutionMethod::Compiled), - ], - ); + let mut group = c.benchmark_group("execute blocks"); + let execution_methods = vec![ + ExecutionMethod::Native, + ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), + #[cfg(feature = "wasmtime")] + ExecutionMethod::Wasm(WasmExecutionMethod::Compiled), + ]; + + for strategy in execution_methods { + group.bench_function( + format!("{:?}", strategy), + |b| { + let genesis_config = node_testing::genesis::config(false, Some(compact_code_unwrap())); + let (use_native, wasm_method) = match strategy { + ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted), + ExecutionMethod::Wasm(wasm_method) => (false, wasm_method), + }; + + let executor = NativeExecutor::new(wasm_method, None, 8); + let runtime_code = RuntimeCode { + code_fetcher: &sp_core::traits::WrappedRuntimeCode(compact_code_unwrap().into()), + hash: vec![1, 2, 3], + heap_pages: None, + }; + + // Get the runtime version to initialize the runtimes cache. + { + let mut test_ext = new_test_ext(&genesis_config); + executor.runtime_version(&mut test_ext.ext(), &runtime_code).unwrap(); + } + + let blocks = test_blocks(&genesis_config, &executor); + + b.iter_batched_ref( + || new_test_ext(&genesis_config), + |test_ext| { + for block in blocks.iter() { + executor.call:: _>( + &mut test_ext.ext(), + &runtime_code, + "Core_execute_block", + &block.0, + use_native, + None, + ).0.unwrap(); + } + }, + BatchSize::LargeInput, + ); + }, + ); + } } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index f0cad60f2614d..a9c37b508e46a 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -44,6 +44,7 @@ frame-benchmarking = { version = "3.1.0", default-features = false, path = "../. frame-support = { version = "3.0.0", default-features = false, path = "../../../frame/support" } frame-system = { version = "3.0.0", default-features = false, path = "../../../frame/system" } frame-system-benchmarking = { version = "3.0.0", default-features = false, path = "../../../frame/system/benchmarking", optional = true } +frame-election-provider-support = { version = "3.0.0", default-features = false, path = "../../../frame/election-provider-support" } frame-system-rpc-runtime-api = { version = "3.0.0", default-features = false, path = "../../../frame/system/rpc/runtime-api/" } frame-try-runtime = { version = "0.9.0", default-features = false, path = "../../../frame/try-runtime", optional = true } pallet-assets = { version = "3.0.0", default-features = false, path = "../../../frame/assets" } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 46f80cc56afd9..464f7e3bcdcfa 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -470,6 +470,7 @@ parameter_types! { pub const MaxNominatorRewardedPerValidator: u32 = 256; } +use frame_election_provider_support::onchain; impl pallet_staking::Config for Runtime { const MAX_NOMINATIONS: u32 = ::LIMIT as u32; @@ -494,6 +495,8 @@ impl pallet_staking::Config for Runtime { type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type ElectionProvider = ElectionProviderMultiPhase; + type GenesisElectionProvider = + onchain::OnChainSequentialPhragmen>; type WeightInfo = pallet_staking::weights::SubstrateWeight; } @@ -504,7 +507,7 @@ parameter_types! { // fallback: no need to do on-chain phragmen initially. pub const Fallback: pallet_election_provider_multi_phase::FallbackStrategy = - pallet_election_provider_multi_phase::FallbackStrategy::OnChain; + pallet_election_provider_multi_phase::FallbackStrategy::Nothing; pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(1u32, 10_000); @@ -878,16 +881,10 @@ impl pallet_im_online::Config for Runtime { type WeightInfo = pallet_im_online::weights::SubstrateWeight; } -parameter_types! { - pub OffencesWeightSoftLimit: Weight = Perbill::from_percent(60) * - RuntimeBlockWeights::get().max_block; -} - impl pallet_offences::Config for Runtime { type Event = Event; type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; - type WeightSoftLimit = OffencesWeightSoftLimit; } impl pallet_authority_discovery::Config for Runtime {} diff --git a/client/chain-spec/src/extension.rs b/client/chain-spec/src/extension.rs index c0352529f8673..2a6126e4ce2ca 100644 --- a/client/chain-spec/src/extension.rs +++ b/client/chain-spec/src/extension.rs @@ -233,7 +233,7 @@ impl Extension for Forks where fn get(&self) -> Option<&T> { match TypeId::of::() { - x if x == TypeId::of::() => Any::downcast_ref(&self.base), + x if x == TypeId::of::() => ::downcast_ref(&self.base), _ => self.base.get(), } } @@ -252,7 +252,7 @@ impl Extension for Forks where <::Extension as Group>::Fork: Extension, { if TypeId::of::() == TypeId::of::() { - Any::downcast_ref(&self.for_type::()?).cloned() + ::downcast_ref(&self.for_type::()?).cloned() } else { self.get::::Extension>>()? .for_type() @@ -275,7 +275,7 @@ impl GetExtension for E { /// Helper function that queries an extension by type from `GetExtension` /// trait object. pub fn get_extension(e: &dyn GetExtension) -> Option<&T> { - Any::downcast_ref(GetExtension::get_any(e, TypeId::of::())) + ::downcast_ref(GetExtension::get_any(e, TypeId::of::())) } #[cfg(test)] diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 351a2b5f40f00..2048a5c4659f2 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -414,7 +414,7 @@ mod tests { authoring_version: 1, spec_version: 1, impl_version: 1, - apis: sp_api::create_apis_vec!([(Core::::ID, 1)]), + apis: sp_api::create_apis_vec!([(>::ID, 1)]), }; let version = decode_version(&old_runtime_version.encode()).unwrap(); @@ -429,7 +429,7 @@ mod tests { authoring_version: 1, spec_version: 1, impl_version: 1, - apis: sp_api::create_apis_vec!([(Core::::ID, 3)]), + apis: sp_api::create_apis_vec!([(>::ID, 3)]), }; decode_version(&old_runtime_version.encode()).unwrap_err(); @@ -443,7 +443,7 @@ mod tests { authoring_version: 1, spec_version: 1, impl_version: 1, - apis: sp_api::create_apis_vec!([(Core::::ID, 3)]), + apis: sp_api::create_apis_vec!([(>::ID, 3)]), transaction_version: 3, }; diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 137f32b5e502c..6aad68572ea73 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -28,8 +28,7 @@ use sp_runtime::{ use frame_system::InitKind; use frame_support::{ parameter_types, - traits::{KeyOwnerProofSystem, OnInitialize}, - weights::Weight, + traits::{KeyOwnerProofSystem, OnInitialize, GenesisBuild}, }; use sp_io; use sp_core::{H256, U256, crypto::{IsWrappedBy, KeyTypeId, Pair}}; @@ -210,19 +209,14 @@ impl pallet_staking::Config for Test { type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type NextNewSession = Session; type ElectionProvider = onchain::OnChainSequentialPhragmen; + type GenesisElectionProvider = Self::ElectionProvider; type WeightInfo = (); } -parameter_types! { - pub OffencesWeightSoftLimit: Weight = Perbill::from_percent(60) - * BlockWeights::get().max_block; -} - impl pallet_offences::Config for Test { type Event = Event; type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; - type WeightSoftLimit = OffencesWeightSoftLimit; } parameter_types! { diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 1609ffa3beef0..24514f265318b 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -269,7 +269,7 @@ pub type CompactAccuracyOf = as CompactSolution>::Accuracy; pub type OnChainAccuracyOf = ::OnChainAccuracy; /// Wrapper type that implements the configurations needed for the on-chain backup. -struct OnChainConfig(sp_std::marker::PhantomData); +pub struct OnChainConfig(sp_std::marker::PhantomData); impl onchain::Config for OnChainConfig { type AccountId = T::AccountId; type BlockNumber = T::BlockNumber; @@ -381,11 +381,11 @@ impl Default for ElectionCompute { #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct RawSolution { /// Compact election edges. - compact: C, + pub compact: C, /// The _claimed_ score of the solution. - score: ElectionScore, + pub score: ElectionScore, /// The round at which this solution should be submitted. - round: u32, + pub round: u32, } impl Default for RawSolution { @@ -402,13 +402,13 @@ pub struct ReadySolution { /// /// This is target-major vector, storing each winners, total backing, and each individual /// backer. - supports: Supports, + pub supports: Supports, /// The score of the solution. /// /// This is needed to potentially challenge the solution. - score: ElectionScore, + pub score: ElectionScore, /// How this election was computed. - compute: ElectionCompute, + pub compute: ElectionCompute, } /// A snapshot of all the data that is needed for en entire round. They are provided by @@ -432,10 +432,10 @@ pub struct RoundSnapshot { pub struct SolutionOrSnapshotSize { /// The length of voters. #[codec(compact)] - voters: u32, + pub voters: u32, /// The length of targets. #[codec(compact)] - targets: u32, + pub targets: u32, } /// Internal errors of the pallet. diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index b570c4482814b..280907ac5439a 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -168,12 +168,16 @@ impl Pallet { size, T::MinerMaxWeight::get(), ); + log!( debug, - "miner: current compact solution voters = {}, maximum_allowed = {}", + "initial solution voters = {}, snapshot = {:?}, maximum_allowed(capped) = {}", compact.voter_count(), + size, maximum_allowed_voters, ); + + // trim weight. let compact = Self::trim_compact(maximum_allowed_voters, compact, &voter_index)?; // re-calc score. @@ -252,10 +256,12 @@ impl Pallet { } } + log!(debug, "removed {} voter to meet the max weight limit.", to_remove); Ok(compact) } _ => { // nada, return as-is + log!(debug, "didn't remove any voter for weight limits."); Ok(compact) } } @@ -298,6 +304,7 @@ impl Pallet { // First binary-search the right amount of voters let mut step = voters / 2; let mut current_weight = weight_with(voters); + while step > 0 { match next_voters(current_weight, voters, step) { // proceed with the binary search @@ -324,13 +331,14 @@ impl Pallet { voters -= 1; } + let final_decision = voters.min(size.voters); debug_assert!( - weight_with(voters.min(size.voters)) <= max_weight, + weight_with(final_decision) <= max_weight, "weight_with({}) <= {}", - voters.min(size.voters), + final_decision, max_weight, ); - voters.min(size.voters) + final_decision } /// Checks if an execution of the offchain worker is permitted at the given block number, or diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 5031cb57e6428..84b7a068c5f2d 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1308,7 +1308,6 @@ mod tests { } fn has_lock(who: &u64) -> u64 { - dbg!(Balances::locks(who)); Balances::locks(who) .get(0) .cloned() diff --git a/frame/elections-phragmen/src/migrations_3_0_0.rs b/frame/elections-phragmen/src/migrations_3_0_0.rs index 8adc4c1a69f7c..8afc9ed66920b 100644 --- a/frame/elections-phragmen/src/migrations_3_0_0.rs +++ b/frame/elections-phragmen/src/migrations_3_0_0.rs @@ -21,7 +21,6 @@ use codec::{Encode, Decode, FullCodec}; use sp_std::prelude::*; use frame_support::{ RuntimeDebug, weights::Weight, Twox64Concat, - storage::types::{StorageMap, StorageValue}, traits::{GetPalletVersion, PalletVersion}, }; @@ -51,38 +50,21 @@ pub trait V2ToV3 { type Balance: 'static + FullCodec + Copy; } -struct __Candidates; -impl frame_support::traits::StorageInstance for __Candidates { - fn pallet_prefix() -> &'static str { "PhragmenElection" } - const STORAGE_PREFIX: &'static str = "Candidates"; -} - -#[allow(type_alias_bounds)] -type Candidates = StorageValue<__Candidates, Vec<(T::AccountId, T::Balance)>>; - -struct __Members; -impl frame_support::traits::StorageInstance for __Members { - fn pallet_prefix() -> &'static str { "PhragmenElection" } - const STORAGE_PREFIX: &'static str = "Members"; -} -#[allow(type_alias_bounds)] -type Members = StorageValue<__Members, Vec>>; - -struct __RunnersUp; -impl frame_support::traits::StorageInstance for __RunnersUp { - fn pallet_prefix() -> &'static str { "PhragmenElection" } - const STORAGE_PREFIX: &'static str = "RunnersUp"; -} -#[allow(type_alias_bounds)] -type RunnersUp = StorageValue<__RunnersUp, Vec>>; - -struct __Voting; -impl frame_support::traits::StorageInstance for __Voting { - fn pallet_prefix() -> &'static str { "PhragmenElection" } - const STORAGE_PREFIX: &'static str = "Voting"; -} -#[allow(type_alias_bounds)] -type Voting = StorageMap<__Voting, Twox64Concat, T::AccountId, Voter>; +frame_support::generate_storage_alias!( + PhragmenElection, Candidates => Value> +); +frame_support::generate_storage_alias!( + PhragmenElection, Members => Value>> +); +frame_support::generate_storage_alias!( + PhragmenElection, RunnersUp => Value>> +); +frame_support::generate_storage_alias!( + PhragmenElection, Voting => Map< + (Twox64Concat, T::AccountId), + Voter + > +); /// Apply all of the migrations from 2_0_0 to 3_0_0. /// diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 277b20cf20bfa..2bf53241751be 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -600,7 +600,7 @@ mod tests { } #[weight = 0] - fn calculate_storage_root(origin) { + fn calculate_storage_root(_origin) { let root = sp_io::storage::root(); sp_io::storage::set("storage_root".as_bytes(), &root); } diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 3f450e18bc783..a9239fd060fa9 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -24,8 +24,7 @@ use ::grandpa as finality_grandpa; use codec::Encode; use frame_support::{ parameter_types, - traits::{KeyOwnerProofSystem, OnFinalize, OnInitialize}, - weights::Weight, + traits::{KeyOwnerProofSystem, OnFinalize, OnInitialize, GenesisBuild}, }; use pallet_staking::EraIndex; use sp_core::{crypto::KeyTypeId, H256}; @@ -216,18 +215,14 @@ impl pallet_staking::Config for Test { type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type NextNewSession = Session; type ElectionProvider = onchain::OnChainSequentialPhragmen; + type GenesisElectionProvider = Self::ElectionProvider; type WeightInfo = (); } -parameter_types! { - pub OffencesWeightSoftLimit: Weight = Perbill::from_percent(60) * BlockWeights::get().max_block; -} - impl pallet_offences::Config for Test { type Event = Event; type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; - type WeightSoftLimit = OffencesWeightSoftLimit; } parameter_types! { diff --git a/frame/merkle-mountain-range/src/lib.rs b/frame/merkle-mountain-range/src/lib.rs index 6992341f6bbd1..a8e707c7ac4e0 100644 --- a/frame/merkle-mountain-range/src/lib.rs +++ b/frame/merkle-mountain-range/src/lib.rs @@ -154,7 +154,7 @@ decl_storage! { decl_module! { /// A public part of the pallet. pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: T::Origin { - fn on_initialize(n: T::BlockNumber) -> Weight { + fn on_initialize(_n: T::BlockNumber) -> Weight { use primitives::LeafDataProvider; let leaves = Self::mmr_leaves(); let peaks_before = mmr::utils::NodesUtils::new(leaves).number_of_peaks(); diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 08517a4ac8df0..0ccf9f73be976 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -26,13 +26,13 @@ use sp_std::vec; use frame_system::{RawOrigin, Pallet as System, Config as SystemConfig}; use frame_benchmarking::{benchmarks, account, impl_benchmark_test_suite}; -use frame_support::traits::{Currency, OnInitialize, ValidatorSet, ValidatorSetWithIdentification}; +use frame_support::traits::{Currency, ValidatorSet, ValidatorSetWithIdentification}; use sp_runtime::{ Perbill, traits::{Convert, StaticLookup, Saturating, UniqueSaturatedInto}, }; -use sp_staking::offence::{ReportOffence, Offence, OffenceDetails}; +use sp_staking::offence::{ReportOffence, Offence}; use pallet_balances::Config as BalancesConfig; use pallet_babe::BabeEquivocationOffence; @@ -42,7 +42,7 @@ use pallet_offences::{Config as OffencesConfig, Module as Offences}; use pallet_session::historical::{Config as HistoricalConfig, IdentificationTuple}; use pallet_session::{Config as SessionConfig, SessionManager}; use pallet_staking::{ - Module as Staking, Config as StakingConfig, RewardDestination, ValidatorPrefs, Exposure, + Pallet as Staking, Config as StakingConfig, RewardDestination, ValidatorPrefs, Exposure, IndividualExposure, Event as StakingEvent, }; @@ -51,7 +51,6 @@ const SEED: u32 = 0; const MAX_REPORTERS: u32 = 100; const MAX_OFFENDERS: u32 = 100; const MAX_NOMINATORS: u32 = 100; -const MAX_DEFERRED_OFFENCES: u32 = 100; pub struct Pallet(Offences); @@ -271,8 +270,6 @@ benchmarks! { ); } verify { - // make sure the report was not deferred - assert!(Offences::::deferred_offences().is_empty()); let bond_amount: u32 = UniqueSaturatedInto::::unique_saturated_into(bond_amount::()); let slash_amount = slash_fraction * bond_amount; let reward_amount = slash_amount * (1 + n) / 2; @@ -306,7 +303,6 @@ benchmarks! { pallet_offences::Event::Offence( UnresponsivenessOffence::::ID, 0_u32.to_le_bytes().to_vec(), - true ) ).into())) ); @@ -336,8 +332,6 @@ benchmarks! { let _ = Offences::::report_offence(reporters, offence); } verify { - // make sure the report was not deferred - assert!(Offences::::deferred_offences().is_empty()); // make sure that all slashes have been applied assert_eq!( System::::event_count(), 0 @@ -372,8 +366,6 @@ benchmarks! { let _ = Offences::::report_offence(reporters, offence); } verify { - // make sure the report was not deferred - assert!(Offences::::deferred_offences().is_empty()); // make sure that all slashes have been applied assert_eq!( System::::event_count(), 0 @@ -383,42 +375,6 @@ benchmarks! { + n // nominators slashed ); } - - on_initialize { - let d in 1 .. MAX_DEFERRED_OFFENCES; - let o = 10; - let n = 100; - - let mut deferred_offences = vec![]; - let offenders = make_offenders::(o, n)?.0; - let offence_details = offenders.into_iter() - .map(|offender| OffenceDetails { - offender: T::convert(offender), - reporters: vec![], - }) - .collect::>(); - - for i in 0 .. d { - let fractions = offence_details.iter() - .map(|_| Perbill::from_percent(100 * (i + 1) / MAX_DEFERRED_OFFENCES)) - .collect::>(); - deferred_offences.push((offence_details.clone(), fractions.clone(), 0u32)); - } - - Offences::::set_deferred_offences(deferred_offences); - assert!(!Offences::::deferred_offences().is_empty()); - }: { - Offences::::on_initialize(0u32.into()); - } - verify { - // make sure that all deferred offences were reported with Ok status. - assert!(Offences::::deferred_offences().is_empty()); - assert_eq!( - System::::event_count(), d * (0 - + o // offenders slashed - + o * n // nominators slashed - )); - } } impl_benchmark_test_suite!( diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 223d6d4d477a6..8f9461cdfbe2a 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -22,7 +22,7 @@ use super::*; use frame_support::{ parameter_types, - weights::{Weight, constants::WEIGHT_PER_SECOND}, + weights::constants::WEIGHT_PER_SECOND, }; use frame_system as system; use sp_runtime::{ @@ -175,6 +175,7 @@ impl pallet_staking::Config for Test { type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type ElectionProvider = onchain::OnChainSequentialPhragmen; + type GenesisElectionProvider = Self::ElectionProvider; type WeightInfo = (); } @@ -188,15 +189,10 @@ impl pallet_im_online::Config for Test { type WeightInfo = (); } -parameter_types! { - pub OffencesWeightSoftLimit: Weight = Perbill::from_percent(60) * BlockWeights::get().max_block; -} - impl pallet_offences::Config for Test { type Event = Event; type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; - type WeightSoftLimit = OffencesWeightSoftLimit; } impl frame_system::offchain::SendTransactionTypes for Test where Call: From { diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 2765c0aaa0ea6..cd25ca1ef1dc4 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -24,12 +24,13 @@ mod mock; mod tests; +mod migration; use sp_std::vec::Vec; use frame_support::{ - decl_module, decl_event, decl_storage, Parameter, traits::Get, weights::Weight, + decl_module, decl_event, decl_storage, Parameter, weights::Weight, }; -use sp_runtime::{traits::{Hash, Zero}, Perbill}; +use sp_runtime::{traits::Hash, Perbill}; use sp_staking::{ SessionIndex, offence::{Offence, ReportOffence, Kind, OnOffenceHandler, OffenceDetails, OffenceError}, @@ -42,13 +43,6 @@ type OpaqueTimeSlot = Vec; /// A type alias for a report identifier. type ReportIdOf = ::Hash; -/// Type of data stored as a deferred offence -pub type DeferredOffenceOf = ( - Vec::AccountId, ::IdentificationTuple>>, - Vec, - SessionIndex, -); - pub trait WeightInfo { fn report_offence_im_online(r: u32, o: u32, n: u32, ) -> Weight; fn report_offence_grandpa(r: u32, n: u32, ) -> Weight; @@ -71,10 +65,6 @@ pub trait Config: frame_system::Config { type IdentificationTuple: Parameter + Ord; /// A handler called for every offence report. type OnOffenceHandler: OnOffenceHandler; - /// The a soft limit on maximum weight that may be consumed while dispatching deferred offences in - /// `on_initialize`. - /// Note it's going to be exceeded before we stop adding to it, so it has to be set conservatively. - type WeightSoftLimit: Get; } decl_storage! { @@ -84,10 +74,6 @@ decl_storage! { map hasher(twox_64_concat) ReportIdOf => Option>; - /// Deferred reports that have been rejected by the offence handler and need to be submitted - /// at a later time. - DeferredOffences get(fn deferred_offences): Vec>; - /// A vector of reports of the same kind that happened at the same time slot. ConcurrentReportsIndex: double_map hasher(twox_64_concat) Kind, hasher(twox_64_concat) OpaqueTimeSlot @@ -106,10 +92,9 @@ decl_storage! { decl_event!( pub enum Event { /// There is an offence reported of the given `kind` happened at the `session_index` and - /// (kind-specific) time slot. This event is not deposited for duplicate slashes. last - /// element indicates of the offence was applied (true) or queued (false) - /// \[kind, timeslot, applied\]. - Offence(Kind, OpaqueTimeSlot, bool), + /// (kind-specific) time slot. This event is not deposited for duplicate slashes. + /// \[kind, timeslot\]. + Offence(Kind, OpaqueTimeSlot), } ); @@ -117,42 +102,8 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn on_initialize(now: T::BlockNumber) -> Weight { - // only decode storage if we can actually submit anything again. - if !T::OnOffenceHandler::can_report() { - return 0; - } - - let limit = T::WeightSoftLimit::get(); - let mut consumed = Weight::zero(); - - >::mutate(|deferred| { - deferred.retain(|(offences, perbill, session)| { - if consumed >= limit { - true - } else { - // keep those that fail to be reported again. An error log is emitted here; this - // should not happen if staking's `can_report` is implemented properly. - match T::OnOffenceHandler::on_offence(&offences, &perbill, *session) { - Ok(weight) => { - consumed += weight; - false - }, - Err(_) => { - log::error!( - target: "runtime::offences", - "re-submitting a deferred slash returned Err at {:?}. \ - This should not happen with pallet-staking", - now, - ); - true - }, - } - } - }) - }); - - consumed + fn on_runtime_upgrade() -> Weight { + migration::remove_deferred_storage::() } } } @@ -187,14 +138,14 @@ where let slash_perbill: Vec<_> = (0..concurrent_offenders.len()) .map(|_| new_fraction.clone()).collect(); - let applied = Self::report_or_store_offence( + T::OnOffenceHandler::on_offence( &concurrent_offenders, &slash_perbill, offence.session_index(), ); // Deposit the event. - Self::deposit_event(Event::Offence(O::ID, time_slot.encode(), applied)); + Self::deposit_event(Event::Offence(O::ID, time_slot.encode())); Ok(()) } @@ -210,28 +161,6 @@ where } impl Module { - /// Tries (without checking) to report an offence. Stores them in [`DeferredOffences`] in case - /// it fails. Returns false in case it has to store the offence. - fn report_or_store_offence( - concurrent_offenders: &[OffenceDetails], - slash_perbill: &[Perbill], - session_index: SessionIndex, - ) -> bool { - match T::OnOffenceHandler::on_offence( - &concurrent_offenders, - &slash_perbill, - session_index, - ) { - Ok(_) => true, - Err(_) => { - >::mutate(|d| - d.push((concurrent_offenders.to_vec(), slash_perbill.to_vec(), session_index)) - ); - false - } - } - } - /// Compute the ID for the given report properties. /// /// The report id depends on the offence kind, time slot and the id of offender. @@ -285,11 +214,6 @@ impl Module { None } } - - #[cfg(feature = "runtime-benchmarks")] - pub fn set_deferred_offences(offences: Vec>) { - >::put(offences); - } } struct TriageOutcome { diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs new file mode 100644 index 0000000000000..ce8a125e7e1a1 --- /dev/null +++ b/frame/offences/src/migration.rs @@ -0,0 +1,99 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{Config, OffenceDetails, Perbill, SessionIndex}; +use frame_support::{traits::Get, weights::Weight, generate_storage_alias}; +use sp_staking::offence::OnOffenceHandler; +use sp_std::vec::Vec; + +/// Type of data stored as a deferred offence +type DeferredOffenceOf = ( + Vec< + OffenceDetails< + ::AccountId, + ::IdentificationTuple, + >, + >, + Vec, + SessionIndex, +); + +// Deferred reports that have been rejected by the offence handler and need to be submitted +// at a later time. +generate_storage_alias!( + Offences, + DeferredOffences => Value>> +); + +pub fn remove_deferred_storage() -> Weight { + let mut weight = T::DbWeight::get().reads_writes(1, 1); + let deferred = >::take(); + log::info!(target: "runtime::offences", "have {} deferred offences, applying.", deferred.len()); + for (offences, perbill, session) in deferred.iter() { + let consumed = T::OnOffenceHandler::on_offence(&offences, &perbill, *session); + weight = weight.saturating_add(consumed); + } + + weight +} + +#[cfg(test)] +mod test { + use super::*; + use crate::mock::{new_test_ext, with_on_offence_fractions, Offences, Runtime as T}; + use frame_support::traits::OnRuntimeUpgrade; + use sp_runtime::Perbill; + use sp_staking::offence::OffenceDetails; + + #[test] + fn should_resubmit_deferred_offences() { + new_test_ext().execute_with(|| { + // given + assert_eq!(>::get().len(), 0); + with_on_offence_fractions(|f| { + assert_eq!(f.clone(), vec![]); + }); + + let offence_details = OffenceDetails::< + ::AccountId, + ::IdentificationTuple, + > { + offender: 5, + reporters: vec![], + }; + + // push deferred offence + >::append(( + vec![offence_details], + vec![Perbill::from_percent(5 + 1 * 100 / 5)], + 1, + )); + + // when + assert_eq!( + Offences::on_runtime_upgrade(), + ::DbWeight::get().reads_writes(1, 2), + ); + + // then + assert!(!>::exists()); + with_on_offence_fractions(|f| { + assert_eq!(f.clone(), vec![Perbill::from_percent(5 + 1 * 100 / 5)]); + }); + }) + } +} diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index ab45bb0837b56..f909977287701 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -40,7 +40,6 @@ pub struct OnOffenceHandler; thread_local! { pub static ON_OFFENCE_PERBILL: RefCell> = RefCell::new(Default::default()); - pub static CAN_REPORT: RefCell = RefCell::new(true); pub static OFFENCE_WEIGHT: RefCell = RefCell::new(Default::default()); } @@ -51,37 +50,21 @@ impl _offenders: &[OffenceDetails], slash_fraction: &[Perbill], _offence_session: SessionIndex, - ) -> Result { - if >::can_report() { - ON_OFFENCE_PERBILL.with(|f| { - *f.borrow_mut() = slash_fraction.to_vec(); - }); - - Ok(OFFENCE_WEIGHT.with(|w| *w.borrow())) - } else { - Err(()) - } - } + ) -> Weight { + ON_OFFENCE_PERBILL.with(|f| { + *f.borrow_mut() = slash_fraction.to_vec(); + }); - fn can_report() -> bool { - CAN_REPORT.with(|c| *c.borrow()) + OFFENCE_WEIGHT.with(|w| *w.borrow()) } } -pub fn set_can_report(can_report: bool) { - CAN_REPORT.with(|c| *c.borrow_mut() = can_report); -} - pub fn with_on_offence_fractions) -> R>(f: F) -> R { ON_OFFENCE_PERBILL.with(|fractions| { f(&mut *fractions.borrow_mut()) }) } -pub fn set_offence_weight(new: Weight) { - OFFENCE_WEIGHT.with(|w| *w.borrow_mut() = new); -} - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -126,16 +109,10 @@ impl frame_system::Config for Runtime { type SS58Prefix = (); } -parameter_types! { - pub OffencesWeightSoftLimit: Weight = - Perbill::from_percent(60) * BlockWeights::get().max_block; -} - impl Config for Runtime { type Event = Event; type IdentificationTuple = u64; type OnOffenceHandler = OnOffenceHandler; - type WeightSoftLimit = OffencesWeightSoftLimit; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index 2b7c500dfa2d9..f7bd90fe93e62 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -22,10 +22,9 @@ use super::*; use crate::mock::{ Offences, System, Offence, Event, KIND, new_test_ext, with_on_offence_fractions, - offence_reports, set_can_report, set_offence_weight, + offence_reports, }; use sp_runtime::Perbill; -use frame_support::traits::OnInitialize; use frame_system::{EventRecord, Phase}; #[test] @@ -132,7 +131,7 @@ fn should_deposit_event() { System::events(), vec![EventRecord { phase: Phase::Initialization, - event: Event::offences(crate::Event::Offence(KIND, time_slot.encode(), true)), + event: Event::offences(crate::Event::Offence(KIND, time_slot.encode())), topics: vec![], }] ); @@ -167,7 +166,7 @@ fn doesnt_deposit_event_for_dups() { System::events(), vec![EventRecord { phase: Phase::Initialization, - event: Event::offences(crate::Event::Offence(KIND, time_slot.encode(), true)), + event: Event::offences(crate::Event::Offence(KIND, time_slot.encode())), topics: vec![], }] ); @@ -285,99 +284,3 @@ fn should_properly_count_offences() { ); }); } - -#[test] -fn should_queue_and_resubmit_rejected_offence() { - new_test_ext().execute_with(|| { - set_can_report(false); - - // will get deferred - let offence = Offence { - validator_set_count: 5, - time_slot: 42, - offenders: vec![5], - }; - Offences::report_offence(vec![], offence).unwrap(); - assert_eq!(Offences::deferred_offences().len(), 1); - // event also indicates unapplied. - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::offences(crate::Event::Offence(KIND, 42u128.encode(), false)), - topics: vec![], - }] - ); - - // will not dequeue - Offences::on_initialize(2); - - // again - let offence = Offence { - validator_set_count: 5, - time_slot: 62, - offenders: vec![5], - }; - Offences::report_offence(vec![], offence).unwrap(); - assert_eq!(Offences::deferred_offences().len(), 2); - - set_can_report(true); - - // can be submitted - let offence = Offence { - validator_set_count: 5, - time_slot: 72, - offenders: vec![5], - }; - Offences::report_offence(vec![], offence).unwrap(); - assert_eq!(Offences::deferred_offences().len(), 2); - - Offences::on_initialize(3); - assert_eq!(Offences::deferred_offences().len(), 0); - }) -} - -#[test] -fn weight_soft_limit_is_used() { - new_test_ext().execute_with(|| { - set_can_report(false); - // Only 2 can fit in one block - set_offence_weight(::WeightSoftLimit::get() / 2); - - // Queue 3 offences - // #1 - let offence = Offence { - validator_set_count: 5, - time_slot: 42, - offenders: vec![5], - }; - Offences::report_offence(vec![], offence).unwrap(); - // #2 - let offence = Offence { - validator_set_count: 5, - time_slot: 62, - offenders: vec![5], - }; - Offences::report_offence(vec![], offence).unwrap(); - // #3 - let offence = Offence { - validator_set_count: 5, - time_slot: 72, - offenders: vec![5], - }; - Offences::report_offence(vec![], offence).unwrap(); - // 3 are queued - assert_eq!(Offences::deferred_offences().len(), 3); - - // Allow reporting - set_can_report(true); - - Offences::on_initialize(3); - // Two are completed, one is left in the queue - assert_eq!(Offences::deferred_offences().len(), 1); - - Offences::on_initialize(4); - // All are done now - assert_eq!(Offences::deferred_offences().len(), 0); - }) -} diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index 52b8ebbdf4780..170c6354e8caa 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -25,6 +25,7 @@ frame-support = { version = "3.0.0", default-features = false, path = "../suppor frame-system = { version = "3.0.0", default-features = false, path = "../system" } pallet-timestamp = { version = "3.0.0", default-features = false, path = "../timestamp" } sp-trie = { version = "3.0.0", optional = true, default-features = false, path = "../../primitives/trie" } +log = { version = "0.4.0", default-features = false } impl-trait-for-tuples = "0.2.1" [dev-dependencies] @@ -46,5 +47,6 @@ std = [ "sp-staking/std", "pallet-timestamp/std", "sp-trie/std", + "log/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index fff3717607f8f..d9a50b431f2e7 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -28,7 +28,6 @@ use sp_std::vec; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; use frame_support::{ codec::Decode, - storage::StorageValue, traits::{KeyOwnerProofSystem, OnInitialize}, }; use frame_system::RawOrigin; @@ -59,7 +58,7 @@ benchmarks! { false, RewardDestination::Staked, )?; - let v_controller = pallet_staking::Module::::bonded(&v_stash).ok_or("not stash")?; + let v_controller = pallet_staking::Pallet::::bonded(&v_stash).ok_or("not stash")?; let keys = T::Keys::default(); let proof: Vec = vec![0,1,2,3]; // Whitelist controller account from further DB operations. @@ -75,7 +74,7 @@ benchmarks! { false, RewardDestination::Staked )?; - let v_controller = pallet_staking::Module::::bonded(&v_stash).ok_or("not stash")?; + let v_controller = pallet_staking::Pallet::::bonded(&v_stash).ok_or("not stash")?; let keys = T::Keys::default(); let proof: Vec = vec![0,1,2,3]; Session::::set_keys(RawOrigin::Signed(v_controller.clone()).into(), keys, proof)?; @@ -125,7 +124,7 @@ fn check_membership_proof_setup( (sp_runtime::KeyTypeId, &'static [u8; 32]), sp_session::MembershipProof, ) { - pallet_staking::ValidatorCount::put(n); + pallet_staking::ValidatorCount::::put(n); // create validators and set random session keys for (n, who) in create_validators::(n, 1000) @@ -137,7 +136,7 @@ fn check_membership_proof_setup( use rand::SeedableRng; let validator = T::Lookup::lookup(who).unwrap(); - let controller = pallet_staking::Module::::bonded(validator).unwrap(); + let controller = pallet_staking::Pallet::::bonded(validator).unwrap(); let keys = { let mut keys = [0u8; 128]; diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 53afeb620c260..1f4e67b279d3f 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -180,6 +180,7 @@ impl pallet_staking::Config for Test { type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type ElectionProvider = onchain::OnChainSequentialPhragmen; + type GenesisElectionProvider = Self::ElectionProvider; type WeightInfo = (); } diff --git a/frame/session/src/historical/mod.rs b/frame/session/src/historical/mod.rs index 8902ebe551f6c..3cfcbf98bf38c 100644 --- a/frame/session/src/historical/mod.rs +++ b/frame/session/src/historical/mod.rs @@ -124,10 +124,17 @@ impl ValidatorSetWithIdentification for Module { /// Specialization of the crate-level `SessionManager` which returns the set of full identification /// when creating a new session. -pub trait SessionManager: crate::SessionManager { +pub trait SessionManager: + crate::SessionManager +{ /// If there was a validator set change, its returns the set of new validators along with their /// full identifications. fn new_session(new_index: SessionIndex) -> Option>; + fn new_session_genesis( + new_index: SessionIndex, + ) -> Option> { + >::new_session(new_index) + } fn start_session(start_index: SessionIndex); fn end_session(end_index: SessionIndex); } @@ -136,19 +143,20 @@ pub trait SessionManager: crate::SessionManager /// sets the historical trie root of the ending session. pub struct NoteHistoricalRoot(sp_std::marker::PhantomData<(T, I)>); -impl crate::SessionManager for NoteHistoricalRoot - where I: SessionManager -{ - fn new_session(new_index: SessionIndex) -> Option> { - +impl> NoteHistoricalRoot { + fn do_new_session(new_index: SessionIndex, is_genesis: bool) -> Option> { StoredRange::mutate(|range| { range.get_or_insert_with(|| (new_index, new_index)).1 = new_index + 1; }); - let new_validators_and_id = >::new_session(new_index); - let new_validators = new_validators_and_id.as_ref().map(|new_validators| { - new_validators.iter().map(|(v, _id)| v.clone()).collect() - }); + let new_validators_and_id = if is_genesis { + >::new_session_genesis(new_index) + } else { + >::new_session(new_index) + }; + let new_validators_opt = new_validators_and_id + .as_ref() + .map(|new_validators| new_validators.iter().map(|(v, _id)| v.clone()).collect()); if let Some(new_validators) = new_validators_and_id { let count = new_validators.len() as ValidatorCount; @@ -166,7 +174,20 @@ impl crate::SessionManager for NoteHistoricalRoot< } } - new_validators + new_validators_opt + } +} + +impl crate::SessionManager for NoteHistoricalRoot +where + I: SessionManager, +{ + fn new_session(new_index: SessionIndex) -> Option> { + Self::do_new_session(new_index, false) + } + + fn new_session_genesis(new_index: SessionIndex) -> Option> { + Self::do_new_session(new_index, true) } fn start_session(start_index: SessionIndex) { diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index e7b16808f7239..8d5af9e15b496 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -238,12 +238,19 @@ pub trait SessionManager { /// `new_session(session)` is guaranteed to be called before `end_session(session-1)`. In other /// words, a new session must always be planned before an ongoing one can be finished. fn new_session(new_index: SessionIndex) -> Option>; + /// Same as `new_session`, but it this should only be called at genesis. + /// + /// The session manager might decide to treat this in a different way. Default impl is simply + /// using [`new_session`]. + fn new_session_genesis(new_index: SessionIndex) -> Option> { + Self::new_session(new_index) + } /// End the session. /// /// Because the session pallet can queue validator set the ending session can be lower than the /// last new session index. fn end_session(end_index: SessionIndex); - /// Start the session. + /// Start an already planned session. /// /// The session start to be used for validation. fn start_session(start_index: SessionIndex); @@ -340,13 +347,9 @@ impl SessionHandler for Tuple { pub struct TestSessionHandler; impl SessionHandler for TestSessionHandler { const KEY_TYPE_IDS: &'static [KeyTypeId] = &[sp_runtime::key_types::DUMMY]; - fn on_genesis_session(_: &[(AId, Ks)]) {} - fn on_new_session(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {} - fn on_before_session_ending() {} - fn on_disabled(_: usize) {} } @@ -449,7 +452,7 @@ decl_storage! { ); } - let initial_validators_0 = T::SessionManager::new_session(0) + let initial_validators_0 = T::SessionManager::new_session_genesis(0) .unwrap_or_else(|| { frame_support::print("No initial validator provided by `SessionManager`, use \ session config keys to generate initial validator set."); @@ -457,7 +460,7 @@ decl_storage! { }); assert!(!initial_validators_0.is_empty(), "Empty validator set for session 0 in genesis block!"); - let initial_validators_1 = T::SessionManager::new_session(1) + let initial_validators_1 = T::SessionManager::new_session_genesis(1) .unwrap_or_else(|| initial_validators_0.clone()); assert!(!initial_validators_1.is_empty(), "Empty validator set for session 1 in genesis block!"); @@ -546,7 +549,7 @@ decl_module! { /// Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed. /// - DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account` /// - DbWrites: `NextKeys`, `origin account` - /// - DbWrites per key id: `KeyOwnder` + /// - DbWrites per key id: `KeyOwner` /// # #[weight = T::WeightInfo::purge_keys()] pub fn purge_keys(origin) { @@ -571,17 +574,17 @@ decl_module! { } impl Module { - /// Move on to next session. Register new validator set and session keys. Changes - /// to the validator set have a session of delay to take effect. This allows for - /// equivocation punishment after a fork. + /// Move on to next session. Register new validator set and session keys. Changes to the + /// validator set have a session of delay to take effect. This allows for equivocation + /// punishment after a fork. pub fn rotate_session() { let session_index = CurrentIndex::get(); + log::trace!(target: "runtime::session", "rotating session {:?}", session_index); let changed = QueuedChanged::get(); // Inform the session handlers that a session is going to end. T::SessionHandler::on_before_session_ending(); - T::SessionManager::end_session(session_index); // Get queued session keys and validators. diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 1d8a5c1fd6451..8adf797abe9e9 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Staking pallet benchmarking. use super::*; -use crate::Module as Staking; +use crate::Pallet as Staking; use testing_utils::*; use sp_runtime::traits::One; @@ -30,6 +30,7 @@ pub use frame_benchmarking::{ const SEED: u32 = 0; const MAX_SPANS: u32 = 100; const MAX_VALIDATORS: u32 = 1000; +const MAX_NOMINATORS: u32 = 1000; const MAX_SLASHES: u32 = 1000; // Add slashing spans to a user account. Not relevant for actual use, only to benchmark @@ -88,10 +89,10 @@ pub fn create_validator_with_nominators( } } - ValidatorCount::put(1); + ValidatorCount::::put(1); // Start a new Era - let new_validators = Staking::::new_era(SessionIndex::one()).unwrap(); + let new_validators = Staking::::try_trigger_new_era(SessionIndex::one(), true).unwrap(); assert_eq!(new_validators.len(), 1); assert_eq!(new_validators[0], v_stash, "Our validator was not selected!"); @@ -102,7 +103,7 @@ pub fn create_validator_with_nominators( individual: points_individual.into_iter().collect(), }; - let current_era = CurrentEra::get().unwrap(); + let current_era = CurrentEra::::get().unwrap(); ErasRewardPoints::::insert(current_era, reward); // Create reward pool @@ -164,7 +165,7 @@ benchmarks! { add_slashing_spans::(&stash, s); let amount = T::Currency::minimum_balance() * 5u32.into(); // Half of total Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; - CurrentEra::put(EraIndex::max_value()); + CurrentEra::::put(EraIndex::max_value()); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_total: BalanceOf = ledger.total; whitelist_account!(controller); @@ -183,7 +184,7 @@ benchmarks! { add_slashing_spans::(&stash, s); let amount = T::Currency::minimum_balance() * 10u32.into(); Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; - CurrentEra::put(EraIndex::max_value()); + CurrentEra::::put(EraIndex::max_value()); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_total: BalanceOf = ledger.total; whitelist_account!(controller); @@ -303,17 +304,17 @@ benchmarks! { let validator_count = MAX_VALIDATORS; }: _(RawOrigin::Root, validator_count) verify { - assert_eq!(ValidatorCount::get(), validator_count); + assert_eq!(ValidatorCount::::get(), validator_count); } force_no_eras {}: _(RawOrigin::Root) - verify { assert_eq!(ForceEra::get(), Forcing::ForceNone); } + verify { assert_eq!(ForceEra::::get(), Forcing::ForceNone); } force_new_era {}: _(RawOrigin::Root) - verify { assert_eq!(ForceEra::get(), Forcing::ForceNew); } + verify { assert_eq!(ForceEra::::get(), Forcing::ForceNew); } force_new_era_always {}: _(RawOrigin::Root) - verify { assert_eq!(ForceEra::get(), Forcing::ForceAlways); } + verify { assert_eq!(ForceEra::::get(), Forcing::ForceAlways); } // Worst case scenario, the list of invulnerables is very long. set_invulnerables { @@ -361,7 +362,7 @@ benchmarks! { RewardDestination::Controller, )?; - let current_era = CurrentEra::get().unwrap(); + let current_era = CurrentEra::::get().unwrap(); // set the commission for this particular era as well. >::insert(current_era, validator.clone(), >::validators(&validator)); @@ -394,7 +395,7 @@ benchmarks! { RewardDestination::Staked, )?; - let current_era = CurrentEra::get().unwrap(); + let current_era = CurrentEra::::get().unwrap(); // set the commission for this particular era as well. >::insert(current_era, validator.clone(), >::validators(&validator)); @@ -444,8 +445,8 @@ benchmarks! { set_history_depth { let e in 1 .. 100; - HistoryDepth::put(e); - CurrentEra::put(e); + HistoryDepth::::put(e); + CurrentEra::::put(e); for i in 0 .. e { >::insert(i, T::AccountId::default(), Exposure::>::default()); >::insert(i, T::AccountId::default(), Exposure::>::default()); @@ -453,22 +454,28 @@ benchmarks! { >::insert(i, BalanceOf::::one()); >::insert(i, EraRewardPoints::::default()); >::insert(i, BalanceOf::::one()); - ErasStartSessionIndex::insert(i, i); + ErasStartSessionIndex::::insert(i, i); } }: _(RawOrigin::Root, EraIndex::zero(), u32::max_value()) verify { - assert_eq!(HistoryDepth::get(), 0); + assert_eq!(HistoryDepth::::get(), 0); } reap_stash { let s in 1 .. MAX_SPANS; let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; add_slashing_spans::(&stash, s); T::Currency::make_free_balance_be(&stash, T::Currency::minimum_balance()); whitelist_account!(controller); + + assert!(Bonded::::contains_key(&stash)); + assert!(Validators::::contains_key(&stash)); + }: _(RawOrigin::Signed(controller), stash.clone(), s) verify { assert!(!Bonded::::contains_key(&stash)); + assert!(!Validators::::contains_key(&stash)); } new_era { @@ -484,7 +491,8 @@ benchmarks! { )?; let session_index = SessionIndex::one(); }: { - let validators = Staking::::new_era(session_index).ok_or("`new_era` failed")?; + let validators = Staking::::try_trigger_new_era(session_index, true) + .ok_or("`new_era` failed")?; assert!(validators.len() == v as usize); } @@ -500,10 +508,10 @@ benchmarks! { None, )?; // Start a new Era - let new_validators = Staking::::new_era(SessionIndex::one()).unwrap(); + let new_validators = Staking::::try_trigger_new_era(SessionIndex::one(), true).unwrap(); assert!(new_validators.len() == v as usize); - let current_era = CurrentEra::get().unwrap(); + let current_era = CurrentEra::::get().unwrap(); let mut points_total = 0; let mut points_individual = Vec::new(); let mut payout_calls_arg = Vec::new(); @@ -562,9 +570,9 @@ benchmarks! { get_npos_voters { // number of validator intention. - let v in 200 .. 400; + let v in (MAX_VALIDATORS / 2) .. MAX_VALIDATORS; // number of nominator intention. - let n in 200 .. 400; + let n in (MAX_NOMINATORS / 2) .. MAX_NOMINATORS; // total number of slashing spans. Assigned to validators randomly. let s in 1 .. 20; @@ -583,15 +591,42 @@ benchmarks! { get_npos_targets { // number of validator intention. - let v in 200 .. 400; + let v in (MAX_VALIDATORS / 2) .. MAX_VALIDATORS; // number of nominator intention. - let n = 500; + let n = MAX_NOMINATORS; let _ = create_validators_with_nominators_for_era::(v, n, T::MAX_NOMINATIONS as usize, false, None)?; }: { let targets = >::get_npos_targets(); assert_eq!(targets.len() as u32, v); } + + update_staking_limits { + // This function always does the same thing... just write to 4 storage items. + }: _( + RawOrigin::Root, + BalanceOf::::max_value(), + BalanceOf::::max_value(), + Some(u32::max_value()), + Some(u32::max_value()) + ) verify { + assert_eq!(MinNominatorBond::::get(), BalanceOf::::max_value()); + assert_eq!(MinValidatorBond::::get(), BalanceOf::::max_value()); + assert_eq!(MaxNominatorsCount::::get(), Some(u32::max_value())); + assert_eq!(MaxValidatorsCount::::get(), Some(u32::max_value())); + } + + chill_other { + let (_, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; + Staking::::update_staking_limits( + RawOrigin::Root.into(), BalanceOf::::max_value(), BalanceOf::::max_value(), None, None, + )?; + let caller = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), controller.clone()) + verify { + assert!(!Validators::::contains_key(controller)); + } } #[cfg(test)] @@ -602,7 +637,7 @@ mod tests { #[test] fn create_validators_with_nominators_for_era_works() { - ExtBuilder::default().has_stakers(true).build().execute_with(|| { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { let v = 10; let n = 100; @@ -624,7 +659,7 @@ mod tests { #[test] fn create_validator_with_nominators_works() { - ExtBuilder::default().has_stakers(true).build().execute_with(|| { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { let n = 10; let (validator_stash, nominators) = create_validator_with_nominators::( @@ -636,7 +671,7 @@ mod tests { assert_eq!(nominators.len() as u32, n); - let current_era = CurrentEra::get().unwrap(); + let current_era = CurrentEra::::get().unwrap(); let original_free_balance = Balances::free_balance(&validator_stash); assert_ok!(Staking::payout_stakers(Origin::signed(1337), validator_stash, current_era)); @@ -648,7 +683,7 @@ mod tests { #[test] fn add_slashing_spans_works() { - ExtBuilder::default().has_stakers(true).build().execute_with(|| { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { let n = 10; let (validator_stash, _nominators) = create_validator_with_nominators::( @@ -679,7 +714,7 @@ mod tests { #[test] fn test_payout_all() { - ExtBuilder::default().has_stakers(true).build().execute_with(|| { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { let v = 10; let n = 100; @@ -699,6 +734,7 @@ mod tests { impl_benchmark_test_suite!( Staking, - crate::mock::ExtBuilder::default().has_stakers(true).build(), + crate::mock::ExtBuilder::default().has_stakers(true), crate::mock::Test, + exec_name = build_and_execute ); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 31735f75ebc14..047ca4121f66e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -15,17 +15,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Staking Module +//! # Staking Pallet //! -//! The Staking module is used to manage funds at stake by network maintainers. +//! The Staking pallet is used to manage funds at stake by network maintainers. //! //! - [`Config`] //! - [`Call`] -//! - [`Module`] +//! - [`Pallet`] //! //! ## Overview //! -//! The Staking module is the means by which a set of network maintainers (known as _authorities_ in +//! The Staking pallet is the means by which a set of network maintainers (known as _authorities_ in //! some contexts and _validators_ in others) are chosen based upon those who voluntarily place //! funds under deposit. Under deposit, those funds are rewarded under normal operation but are held //! at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging @@ -59,7 +59,7 @@ //! //! #### Staking //! -//! Almost any interaction with the Staking module requires a process of _**bonding**_ (also known +//! Almost any interaction with the Staking pallet requires a process of _**bonding**_ (also known //! as being a _staker_). To become *bonded*, a fund-holding account known as the _stash account_, //! which holds some or all of the funds that become frozen in place as part of the staking process, //! is paired with an active **controller** account, which issues instructions on how they shall be @@ -102,7 +102,7 @@ //! //! #### Rewards and Slash //! -//! The **reward and slashing** procedure is the core of the Staking module, attempting to _embrace +//! The **reward and slashing** procedure is the core of the Staking pallet, attempting to _embrace //! valid behavior_ while _punishing any misbehavior or lack of availability_. //! //! Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the @@ -115,7 +115,7 @@ //! determined, a value is deducted from the balance of the validator and all the nominators who //! voted for this validator (values are deducted from the _stash_ account of the slashed entity). //! -//! Slashing logic is further described in the documentation of the `slashing` module. +//! Slashing logic is further described in the documentation of the `slashing` pallet. //! //! Similar to slashing, rewards are also shared among a validator and its associated nominators. //! Yet, the reward funds are not always transferred to the stash account and can be configured. See @@ -131,19 +131,19 @@ //! //! ### Session managing //! -//! The module implement the trait `SessionManager`. Which is the only API to query new validator +//! The pallet implement the trait `SessionManager`. Which is the only API to query new validator //! set and allowing these validator set to be rewarded once their era is ended. //! //! ## Interface //! //! ### Dispatchable Functions //! -//! The dispatchable functions of the Staking module enable the steps needed for entities to accept -//! and change their role, alongside some helper functions to get/set the metadata of the module. +//! The dispatchable functions of the Staking pallet enable the steps needed for entities to accept +//! and change their role, alongside some helper functions to get/set the metadata of the pallet. //! //! ### Public Functions //! -//! The Staking module contains many public storage items and (im)mutable functions. +//! The Staking pallet contains many public storage items and (im)mutable functions. //! //! ## Usage //! @@ -162,7 +162,7 @@ //! #[weight = 0] //! pub fn reward_myself(origin) -> dispatch::DispatchResult { //! let reported = ensure_signed(origin)?; -//! >::reward_by_ids(vec![(reported, 10)]); +//! >::reward_by_ids(vec![(reported, 10)]); //! Ok(()) //! } //! } @@ -198,9 +198,9 @@ //! //! Total reward is split among validators and their nominators depending on the number of points //! they received during the era. Points are added to a validator using -//! [`reward_by_ids`](Module::reward_by_ids). +//! [`reward_by_ids`](Pallet::reward_by_ids). //! -//! [`Module`] implements +//! [`Pallet`] implements //! [`pallet_authorship::EventHandler`] to add reward //! points to block producer and block producer of referenced uncles. //! @@ -255,14 +255,14 @@ //! //! ## GenesisConfig //! -//! The Staking module depends on the [`GenesisConfig`]. The +//! The Staking pallet depends on the [`GenesisConfig`]. The //! `GenesisConfig` is optional and allow to set some initial stakers. //! //! ## Related Modules //! //! - [Balances](../pallet_balances/index.html): Used to manage values at stake. //! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new -//! validators is stored in the Session module's `Validators` at the end of each era. +//! validators is stored in the Session pallet's `Validators` at the end of each era. #![recursion_limit = "128"] #![cfg_attr(not(feature = "std"), no_std)] @@ -288,13 +288,11 @@ use sp_std::{ }; use codec::{HasCompact, Encode, Decode}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, + pallet_prelude::*, weights::{ - Weight, + Weight, WithPostDispatchInfo, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}, }, - storage::IterableStorageMap, - dispatch::{DispatchResult, DispatchResultWithPostInfo}, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, UnixTime, EstimateNextNewSession, EnsureOrigin, CurrencyToVote, @@ -306,7 +304,7 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, - AtLeast32BitUnsigned, + AtLeast32BitUnsigned, Bounded, }, }; use sp_staking::{ @@ -316,11 +314,12 @@ use sp_staking::{ #[cfg(feature = "std")] use sp_runtime::{Serialize, Deserialize}; use frame_system::{ - self as system, ensure_signed, ensure_root, + ensure_signed, ensure_root, pallet_prelude::*, offchain::SendTransactionTypes, }; use frame_election_provider_support::{ElectionProvider, VoteWeight, Supports, data_provider}; pub use weights::WeightInfo; +pub use pallet::*; const STAKING_ID: LockIdentifier = *b"staking "; pub(crate) const LOG_TARGET: &'static str = "runtime::staking"; @@ -344,7 +343,7 @@ pub type EraIndex = u32; /// Counter for the number of "reward" points earned by a given validator. pub type RewardPoint = u32; -/// The balance type of this module. +/// The balance type of this pallet. pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -545,7 +544,7 @@ impl StakingLedger where if !slash_from_target.is_zero() { *target -= slash_from_target; - // don't leave a dust balance in the staking system. + // Don't leave a dust balance in the staking system. if *target <= minimum_balance { slash_from_target += *target; *value += sp_std::mem::replace(target, Zero::zero()); @@ -563,10 +562,10 @@ impl StakingLedger where slash_out_of(total, &mut chunk.value, &mut value); chunk.value }) - .take_while(|value| value.is_zero()) // take all fully-consumed chunks out. + .take_while(|value| value.is_zero()) // Take all fully-consumed chunks out. .count(); - // kill all drained chunks. + // Kill all drained chunks. let _ = self.unlocking.drain(..i); pre_total.saturating_sub(*total) @@ -656,15 +655,15 @@ impl SessionInterface<::AccountId> for T w Convert<::AccountId, Option<::AccountId>>, { fn disable_validator(validator: &::AccountId) -> Result { - >::disable(validator) + >::disable(validator) } fn validators() -> Vec<::AccountId> { - >::validators() + >::validators() } fn prune_historical_up_to(up_to: SessionIndex) { - >::prune_up_to(up_to); + >::prune_up_to(up_to); } } @@ -715,82 +714,6 @@ impl< } } -pub trait Config: frame_system::Config + SendTransactionTypes> { - /// The staking balance. - type Currency: LockableCurrency; - - /// Time used for computing era duration. - /// - /// It is guaranteed to start being called from the first `on_finalize`. Thus value at genesis - /// is not used. - type UnixTime: UnixTime; - - /// Convert a balance into a number used for election calculation. This must fit into a `u64` - /// but is allowed to be sensibly lossy. The `u64` is used to communicate with the - /// [`sp_npos_elections`] crate which accepts u64 numbers and does operations in 128. - /// Consequently, the backward convert is used convert the u128s from sp-elections back to a - /// [`BalanceOf`]. - type CurrencyToVote: CurrencyToVote>; - - /// Something that provides the election functionality. - type ElectionProvider: frame_election_provider_support::ElectionProvider< - Self::AccountId, - Self::BlockNumber, - // we only accept an election provider that has staking as data provider. - DataProvider = Module, - >; - - /// Maximum number of nominations per nominator. - const MAX_NOMINATIONS: u32; - - /// Tokens have been minted and are unused for validator-reward. - /// See [Era payout](./index.html#era-payout). - type RewardRemainder: OnUnbalanced>; - - /// The overarching event type. - type Event: From> + Into<::Event>; - - /// Handler for the unbalanced reduction when slashing a staker. - type Slash: OnUnbalanced>; - - /// Handler for the unbalanced increment when rewarding a staker. - type Reward: OnUnbalanced>; - - /// Number of sessions per era. - type SessionsPerEra: Get; - - /// Number of eras that staked funds must remain bonded for. - type BondingDuration: Get; - - /// Number of eras that slashes are deferred by, after computation. - /// - /// This should be less than the bonding duration. Set to 0 if slashes - /// should be applied immediately, without opportunity for intervention. - type SlashDeferDuration: Get; - - /// The origin which can cancel a deferred slash. Root can always do this. - type SlashCancelOrigin: EnsureOrigin; - - /// Interface for interacting with a session module. - type SessionInterface: self::SessionInterface; - - /// The payout for validators and the system for the current era. - /// See [Era payout](./index.html#era-payout). - type EraPayout: EraPayout>; - - /// Something that can estimate the next session change, accurately or as a best effort guess. - type NextNewSession: EstimateNextNewSession; - - /// The maximum number of nominators rewarded for each validator. - /// - /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim - /// their reward. This used to limit the i/o cost for the nominator payout. - type MaxNominatorRewardedPerValidator: Get; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; -} - /// Mode of era-forcing. #[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -798,6 +721,8 @@ pub enum Forcing { /// Not forcing anything - just let whatever happen. NotForcing, /// Force a new era, then reset to `NotForcing` as soon as it is done. + /// Note that this will force to trigger an election until a new era is triggered, if the + /// election failed, the next session end will trigger a new election again, until success. ForceNew, /// Avoid a new era indefinitely. ForceNone, @@ -822,191 +747,529 @@ enum Releases { V4_0_0, V5_0_0, // blockable validators. V6_0_0, // removal of all storage associated with offchain phragmen. + V7_0_0, // keep track of number of nominators / validators in map } impl Default for Releases { fn default() -> Self { - Releases::V6_0_0 + Releases::V7_0_0 } } -decl_storage! { - trait Store for Module as Staking { - /// Number of eras to keep in history. - /// - /// Information is kept for eras in `[current_era - history_depth; current_era]`. - /// - /// Must be more than the number of eras delayed by session otherwise. I.e. active era must - /// always be in history. I.e. `active_era > current_era - history_depth` must be - /// guaranteed. - HistoryDepth get(fn history_depth) config(): u32 = 84; +pub mod migrations { + use super::*; - /// The ideal number of staking participants. - pub ValidatorCount get(fn validator_count) config(): u32; + pub mod v7 { + use super::*; - /// Minimum number of staking participants before emergency conditions are imposed. - pub MinimumValidatorCount get(fn minimum_validator_count) config(): u32; + pub fn pre_migrate() -> Result<(), &'static str> { + assert!(CounterForValidators::::get().is_zero(), "CounterForValidators already set."); + assert!(CounterForNominators::::get().is_zero(), "CounterForNominators already set."); + assert!(StorageVersion::::get() == Releases::V6_0_0); + Ok(()) + } - /// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're - /// easy to initialize and the performance hit is minimal (we expect no more than four - /// invulnerables) and restricted to testnets. - pub Invulnerables get(fn invulnerables) config(): Vec; + pub fn migrate() -> Weight { + log!(info, "Migrating staking to Releases::V7_0_0"); + let validator_count = Validators::::iter().count() as u32; + let nominator_count = Nominators::::iter().count() as u32; - /// Map from all locked "stash" accounts to the controller account. - pub Bonded get(fn bonded): map hasher(twox_64_concat) T::AccountId => Option; + CounterForValidators::::put(validator_count); + CounterForNominators::::put(nominator_count); - /// Map from all (unlocked) "controller" accounts to the info regarding the staking. - pub Ledger get(fn ledger): - map hasher(blake2_128_concat) T::AccountId - => Option>>; + StorageVersion::::put(Releases::V7_0_0); + log!(info, "Completed staking migration to Releases::V7_0_0"); - /// Where the reward payment should be made. Keyed by stash. - pub Payee get(fn payee): map hasher(twox_64_concat) T::AccountId => RewardDestination; + T::DbWeight::get().reads_writes( + validator_count.saturating_add(nominator_count).into(), + 2, + ) + } + } - /// The map from (wannabe) validator stash key to the preferences of that validator. - pub Validators get(fn validators): - map hasher(twox_64_concat) T::AccountId => ValidatorPrefs; + pub mod v6 { + use super::*; + use frame_support::{traits::Get, weights::Weight, generate_storage_alias}; - /// The map from nominator stash key to the set of stash keys of all validators to nominate. - pub Nominators get(fn nominators): - map hasher(twox_64_concat) T::AccountId => Option>; + // NOTE: value type doesn't matter, we just set it to () here. + generate_storage_alias!(Staking, SnapshotValidators => Value<()>); + generate_storage_alias!(Staking, SnapshotNominators => Value<()>); + generate_storage_alias!(Staking, QueuedElected => Value<()>); + generate_storage_alias!(Staking, QueuedScore => Value<()>); + generate_storage_alias!(Staking, EraElectionStatus => Value<()>); + generate_storage_alias!(Staking, IsCurrentSessionFinal => Value<()>); - /// The current era index. - /// - /// This is the latest planned era, depending on how the Session pallet queues the validator - /// set, it might be active or not. - pub CurrentEra get(fn current_era): Option; + /// check to execute prior to migration. + pub fn pre_migrate() -> Result<(), &'static str> { + // these may or may not exist. + log!(info, "SnapshotValidators.exits()? {:?}", SnapshotValidators::exists()); + log!(info, "SnapshotNominators.exits()? {:?}", SnapshotNominators::exists()); + log!(info, "QueuedElected.exits()? {:?}", QueuedElected::exists()); + log!(info, "QueuedScore.exits()? {:?}", QueuedScore::exists()); + // these must exist. + assert!(IsCurrentSessionFinal::exists(), "IsCurrentSessionFinal storage item not found!"); + assert!(EraElectionStatus::exists(), "EraElectionStatus storage item not found!"); + Ok(()) + } - /// The active era information, it holds index and start. - /// - /// The active era is the era being currently rewarded. Validator set of this era must be - /// equal to [`SessionInterface::validators`]. - pub ActiveEra get(fn active_era): Option; + /// Migrate storage to v6. + pub fn migrate() -> Weight { + log!(info, "Migrating staking to Releases::V6_0_0"); - /// The session index at which the era start for the last `HISTORY_DEPTH` eras. - /// - /// Note: This tracks the starting session (i.e. session index when era start being active) - /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`. - pub ErasStartSessionIndex get(fn eras_start_session_index): - map hasher(twox_64_concat) EraIndex => Option; + SnapshotValidators::kill(); + SnapshotNominators::kill(); + QueuedElected::kill(); + QueuedScore::kill(); + EraElectionStatus::kill(); + IsCurrentSessionFinal::kill(); - /// Exposure of validator at era. - /// - /// This is keyed first by the era index to allow bulk deletion and then the stash account. - /// - /// Is it removed after `HISTORY_DEPTH` eras. - /// If stakers hasn't been set or has been removed then empty exposure is returned. - pub ErasStakers get(fn eras_stakers): - double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId - => Exposure>; + StorageVersion::::put(Releases::V6_0_0); + log!(info, "Done."); + T::DbWeight::get().writes(6 + 1) + } + } +} - /// Clipped Exposure of validator at era. - /// - /// This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the - /// `T::MaxNominatorRewardedPerValidator` biggest stakers. - /// (Note: the field `total` and `own` of the exposure remains unchanged). - /// This is used to limit the i/o cost for the nominator payout. - /// - /// This is keyed fist by the era index to allow bulk deletion and then the stash account. - /// - /// Is it removed after `HISTORY_DEPTH` eras. - /// If stakers hasn't been set or has been removed then empty exposure is returned. - pub ErasStakersClipped get(fn eras_stakers_clipped): - double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId - => Exposure>; +#[frame_support::pallet] +pub mod pallet { + use super::*; - /// Similar to `ErasStakers`, this holds the preferences of validators. - /// - /// This is keyed first by the era index to allow bulk deletion and then the stash account. - /// - /// Is it removed after `HISTORY_DEPTH` eras. - // If prefs hasn't been set or has been removed then 0 commission is returned. - pub ErasValidatorPrefs get(fn eras_validator_prefs): - double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId - => ValidatorPrefs; + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + SendTransactionTypes> { + /// The staking balance. + type Currency: LockableCurrency; + + /// Time used for computing era duration. + /// + /// It is guaranteed to start being called from the first `on_finalize`. Thus value at genesis + /// is not used. + type UnixTime: UnixTime; + + /// Convert a balance into a number used for election calculation. This must fit into a `u64` + /// but is allowed to be sensibly lossy. The `u64` is used to communicate with the + /// [`sp_npos_elections`] crate which accepts u64 numbers and does operations in 128. + /// Consequently, the backward convert is used convert the u128s from sp-elections back to a + /// [`BalanceOf`]. + type CurrencyToVote: CurrencyToVote>; + + /// Something that provides the election functionality. + type ElectionProvider: frame_election_provider_support::ElectionProvider< + Self::AccountId, + Self::BlockNumber, + // we only accept an election provider that has staking as data provider. + DataProvider = Pallet, + >; + + /// Something that provides the election functionality at genesis. + type GenesisElectionProvider: frame_election_provider_support::ElectionProvider< + Self::AccountId, + Self::BlockNumber, + DataProvider = Pallet, + >; - /// The total validator era payout for the last `HISTORY_DEPTH` eras. - /// - /// Eras that haven't finished yet or has been removed doesn't have reward. - pub ErasValidatorReward get(fn eras_validator_reward): - map hasher(twox_64_concat) EraIndex => Option>; + /// Maximum number of nominations per nominator. + const MAX_NOMINATIONS: u32; + + /// Tokens have been minted and are unused for validator-reward. + /// See [Era payout](./index.html#era-payout). + type RewardRemainder: OnUnbalanced>; + + /// The overarching event type. + type Event: From> + IsType<::Event>; - /// Rewards for the last `HISTORY_DEPTH` eras. - /// If reward hasn't been set or has been removed then 0 reward is returned. - pub ErasRewardPoints get(fn eras_reward_points): - map hasher(twox_64_concat) EraIndex => EraRewardPoints; + /// Handler for the unbalanced reduction when slashing a staker. + type Slash: OnUnbalanced>; - /// The total amount staked for the last `HISTORY_DEPTH` eras. - /// If total hasn't been set or has been removed then 0 stake is returned. - pub ErasTotalStake get(fn eras_total_stake): - map hasher(twox_64_concat) EraIndex => BalanceOf; + /// Handler for the unbalanced increment when rewarding a staker. + type Reward: OnUnbalanced>; + + /// Number of sessions per era. + #[pallet::constant] + type SessionsPerEra: Get; - /// Mode of era forcing. - pub ForceEra get(fn force_era) config(): Forcing; + /// Number of eras that staked funds must remain bonded for. + #[pallet::constant] + type BondingDuration: Get; - /// The percentage of the slash that is distributed to reporters. + /// Number of eras that slashes are deferred by, after computation. /// - /// The rest of the slashed value is handled by the `Slash`. - pub SlashRewardFraction get(fn slash_reward_fraction) config(): Perbill; + /// This should be less than the bonding duration. Set to 0 if slashes + /// should be applied immediately, without opportunity for intervention. + #[pallet::constant] + type SlashDeferDuration: Get; + + /// The origin which can cancel a deferred slash. Root can always do this. + type SlashCancelOrigin: EnsureOrigin; - /// The amount of currency given to reporters of a slash event which was - /// canceled by extraordinary circumstances (e.g. governance). - pub CanceledSlashPayout get(fn canceled_payout) config(): BalanceOf; + /// Interface for interacting with a session pallet. + type SessionInterface: self::SessionInterface; - /// All unapplied slashes that are queued for later. - pub UnappliedSlashes: - map hasher(twox_64_concat) EraIndex => Vec>>; + /// The payout for validators and the system for the current era. + /// See [Era payout](./index.html#era-payout). + type EraPayout: EraPayout>; - /// A mapping from still-bonded eras to the first session index of that era. + /// Something that can estimate the next session change, accurately or as a best effort guess. + type NextNewSession: EstimateNextNewSession; + + /// The maximum number of nominators rewarded for each validator. /// - /// Must contains information for eras for the range: - /// `[active_era - bounding_duration; active_era]` - BondedEras: Vec<(EraIndex, SessionIndex)>; + /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim + /// their reward. This used to limit the i/o cost for the nominator payout. + #[pallet::constant] + type MaxNominatorRewardedPerValidator: Get; - /// All slashing events on validators, mapped by era to the highest slash proportion - /// and slash value of the era. - ValidatorSlashInEra: - double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId - => Option<(Perbill, BalanceOf)>; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } - /// All slashing events on nominators, mapped by era to the highest slash value of the era. - NominatorSlashInEra: - double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId - => Option>; + #[pallet::extra_constants] + impl Pallet { + //TODO: rename to snake case after https://github.com/paritytech/substrate/issues/8826 fixed. + #[allow(non_snake_case)] + fn MaxNominations() -> u32 { + T::MAX_NOMINATIONS + } + } - /// Slashing spans for stash accounts. - SlashingSpans get(fn slashing_spans): map hasher(twox_64_concat) T::AccountId => Option; + #[pallet::type_value] + pub(crate) fn HistoryDepthOnEmpty() -> u32 { 84u32 } - /// Records information about the maximum slash of a stash within a slashing span, - /// as well as how much reward has been paid out. - SpanSlash: - map hasher(twox_64_concat) (T::AccountId, slashing::SpanIndex) - => slashing::SpanRecord>; + /// Number of eras to keep in history. + /// + /// Information is kept for eras in `[current_era - history_depth; current_era]`. + /// + /// Must be more than the number of eras delayed by session otherwise. I.e. active era must + /// always be in history. I.e. `active_era > current_era - history_depth` must be + /// guaranteed. + #[pallet::storage] + #[pallet::getter(fn history_depth)] + pub(crate) type HistoryDepth = StorageValue<_, u32, ValueQuery, HistoryDepthOnEmpty>; + + /// The ideal number of staking participants. + #[pallet::storage] + #[pallet::getter(fn validator_count)] + pub type ValidatorCount = StorageValue<_, u32, ValueQuery>; + + /// Minimum number of staking participants before emergency conditions are imposed. + #[pallet::storage] + #[pallet::getter(fn minimum_validator_count)] + pub type MinimumValidatorCount = StorageValue<_, u32, ValueQuery>; + + /// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're + /// easy to initialize and the performance hit is minimal (we expect no more than four + /// invulnerables) and restricted to testnets. + #[pallet::storage] + #[pallet::getter(fn invulnerables)] + pub type Invulnerables = StorageValue<_, Vec, ValueQuery>; + + /// Map from all locked "stash" accounts to the controller account. + #[pallet::storage] + #[pallet::getter(fn bonded)] + pub type Bonded = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; + + /// The minimum active bond to become and maintain the role of a nominator. + #[pallet::storage] + pub type MinNominatorBond = StorageValue<_, BalanceOf, ValueQuery>; + + /// The minimum active bond to become and maintain the role of a validator. + #[pallet::storage] + pub type MinValidatorBond = StorageValue<_, BalanceOf, ValueQuery>; + + /// Map from all (unlocked) "controller" accounts to the info regarding the staking. + #[pallet::storage] + #[pallet::getter(fn ledger)] + pub type Ledger = StorageMap< + _, + Blake2_128Concat, T::AccountId, + StakingLedger>, + >; - /// The earliest era for which we have a pending, unapplied slash. - EarliestUnappliedSlash: Option; + /// Where the reward payment should be made. Keyed by stash. + #[pallet::storage] + #[pallet::getter(fn payee)] + pub type Payee = StorageMap< + _, + Twox64Concat, T::AccountId, + RewardDestination, + ValueQuery, + >; - /// The last planned session scheduled by the session pallet. - /// - /// This is basically in sync with the call to [`SessionManager::new_session`]. - pub CurrentPlannedSession get(fn current_planned_session): SessionIndex; + /// The map from (wannabe) validator stash key to the preferences of that validator. + /// + /// When updating this storage item, you must also update the `CounterForValidators`. + #[pallet::storage] + #[pallet::getter(fn validators)] + pub type Validators = StorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>; - /// True if network has been upgraded to this version. - /// Storage version of the pallet. - /// - /// This is set to v6.0.0 for new networks. - StorageVersion build(|_: &GenesisConfig| Releases::V6_0_0): Releases; + /// A tracker to keep count of the number of items in the `Validators` map. + #[pallet::storage] + pub type CounterForValidators = StorageValue<_, u32, ValueQuery>; + + /// The maximum validator count before we stop allowing new validators to join. + /// + /// When this value is not set, no limits are enforced. + #[pallet::storage] + pub type MaxValidatorsCount = StorageValue<_, u32, OptionQuery>; + + /// The map from nominator stash key to the set of stash keys of all validators to nominate. + /// + /// When updating this storage item, you must also update the `CounterForNominators`. + #[pallet::storage] + #[pallet::getter(fn nominators)] + pub type Nominators = StorageMap<_, Twox64Concat, T::AccountId, Nominations>; + + /// A tracker to keep count of the number of items in the `Nominators` map. + #[pallet::storage] + pub type CounterForNominators = StorageValue<_, u32, ValueQuery>; + + /// The maximum nominator count before we stop allowing new validators to join. + /// + /// When this value is not set, no limits are enforced. + #[pallet::storage] + pub type MaxNominatorsCount = StorageValue<_, u32, OptionQuery>; + + /// The current era index. + /// + /// This is the latest planned era, depending on how the Session pallet queues the validator + /// set, it might be active or not. + #[pallet::storage] + #[pallet::getter(fn current_era)] + pub type CurrentEra = StorageValue<_, EraIndex>; + + /// The active era information, it holds index and start. + /// + /// The active era is the era being currently rewarded. Validator set of this era must be + /// equal to [`SessionInterface::validators`]. + #[pallet::storage] + #[pallet::getter(fn active_era)] + pub type ActiveEra = StorageValue<_, ActiveEraInfo>; + + /// The session index at which the era start for the last `HISTORY_DEPTH` eras. + /// + /// Note: This tracks the starting session (i.e. session index when era start being active) + /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`. + #[pallet::storage] + #[pallet::getter(fn eras_start_session_index)] + pub type ErasStartSessionIndex = StorageMap<_, Twox64Concat, EraIndex, SessionIndex>; + + /// Exposure of validator at era. + /// + /// This is keyed first by the era index to allow bulk deletion and then the stash account. + /// + /// Is it removed after `HISTORY_DEPTH` eras. + /// If stakers hasn't been set or has been removed then empty exposure is returned. + #[pallet::storage] + #[pallet::getter(fn eras_stakers)] + pub type ErasStakers = StorageDoubleMap< + _, + Twox64Concat, EraIndex, + Twox64Concat, T::AccountId, + Exposure>, + ValueQuery, + >; + + /// Clipped Exposure of validator at era. + /// + /// This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the + /// `T::MaxNominatorRewardedPerValidator` biggest stakers. + /// (Note: the field `total` and `own` of the exposure remains unchanged). + /// This is used to limit the i/o cost for the nominator payout. + /// + /// This is keyed fist by the era index to allow bulk deletion and then the stash account. + /// + /// Is it removed after `HISTORY_DEPTH` eras. + /// If stakers hasn't been set or has been removed then empty exposure is returned. + #[pallet::storage] + #[pallet::getter(fn eras_stakers_clipped)] + pub type ErasStakersClipped = StorageDoubleMap< + _, + Twox64Concat, EraIndex, + Twox64Concat, T::AccountId, + Exposure>, + ValueQuery, + >; + + /// Similar to `ErasStakers`, this holds the preferences of validators. + /// + /// This is keyed first by the era index to allow bulk deletion and then the stash account. + /// + /// Is it removed after `HISTORY_DEPTH` eras. + // If prefs hasn't been set or has been removed then 0 commission is returned. + #[pallet::storage] + #[pallet::getter(fn eras_validator_prefs)] + pub type ErasValidatorPrefs = StorageDoubleMap< + _, + Twox64Concat, EraIndex, + Twox64Concat, T::AccountId, + ValidatorPrefs, + ValueQuery, + >; + + /// The total validator era payout for the last `HISTORY_DEPTH` eras. + /// + /// Eras that haven't finished yet or has been removed doesn't have reward. + #[pallet::storage] + #[pallet::getter(fn eras_validator_reward)] + pub type ErasValidatorReward = StorageMap<_, Twox64Concat, EraIndex, BalanceOf>; + + /// Rewards for the last `HISTORY_DEPTH` eras. + /// If reward hasn't been set or has been removed then 0 reward is returned. + #[pallet::storage] + #[pallet::getter(fn eras_reward_points)] + pub type ErasRewardPoints = StorageMap< + _, + Twox64Concat, EraIndex, + EraRewardPoints, + ValueQuery, + >; + + /// The total amount staked for the last `HISTORY_DEPTH` eras. + /// If total hasn't been set or has been removed then 0 stake is returned. + #[pallet::storage] + #[pallet::getter(fn eras_total_stake)] + pub type ErasTotalStake = StorageMap<_, Twox64Concat, EraIndex, BalanceOf, ValueQuery>; + + /// Mode of era forcing. + #[pallet::storage] + #[pallet::getter(fn force_era)] + pub type ForceEra = StorageValue<_, Forcing, ValueQuery>; + + /// The percentage of the slash that is distributed to reporters. + /// + /// The rest of the slashed value is handled by the `Slash`. + #[pallet::storage] + #[pallet::getter(fn slash_reward_fraction)] + pub type SlashRewardFraction = StorageValue<_, Perbill, ValueQuery>; + + /// The amount of currency given to reporters of a slash event which was + /// canceled by extraordinary circumstances (e.g. governance). + #[pallet::storage] + #[pallet::getter(fn canceled_payout)] + pub type CanceledSlashPayout = StorageValue<_, BalanceOf, ValueQuery>; + + /// All unapplied slashes that are queued for later. + #[pallet::storage] + pub type UnappliedSlashes = StorageMap< + _, + Twox64Concat, EraIndex, + Vec>>, + ValueQuery, + >; + + /// A mapping from still-bonded eras to the first session index of that era. + /// + /// Must contains information for eras for the range: + /// `[active_era - bounding_duration; active_era]` + #[pallet::storage] + pub(crate) type BondedEras = StorageValue<_, Vec<(EraIndex, SessionIndex)>, ValueQuery>; + + /// All slashing events on validators, mapped by era to the highest slash proportion + /// and slash value of the era. + #[pallet::storage] + pub(crate) type ValidatorSlashInEra = StorageDoubleMap< + _, + Twox64Concat, EraIndex, + Twox64Concat, T::AccountId, + (Perbill, BalanceOf), + >; + + /// All slashing events on nominators, mapped by era to the highest slash value of the era. + #[pallet::storage] + pub(crate) type NominatorSlashInEra = StorageDoubleMap< + _, + Twox64Concat, EraIndex, + Twox64Concat, T::AccountId, + BalanceOf, + >; + + /// Slashing spans for stash accounts. + #[pallet::storage] + pub(crate) type SlashingSpans = StorageMap<_, Twox64Concat, T::AccountId, slashing::SlashingSpans>; + + /// Records information about the maximum slash of a stash within a slashing span, + /// as well as how much reward has been paid out. + #[pallet::storage] + pub(crate) type SpanSlash = StorageMap< + _, + Twox64Concat, (T::AccountId, slashing::SpanIndex), + slashing::SpanRecord>, + ValueQuery, + >; + + /// The earliest era for which we have a pending, unapplied slash. + #[pallet::storage] + pub(crate) type EarliestUnappliedSlash = StorageValue<_, EraIndex>; + + /// The last planned session scheduled by the session pallet. + /// + /// This is basically in sync with the call to [`SessionManager::new_session`]. + #[pallet::storage] + #[pallet::getter(fn current_planned_session)] + pub type CurrentPlannedSession = StorageValue<_, SessionIndex, ValueQuery>; + + /// True if network has been upgraded to this version. + /// Storage version of the pallet. + /// + /// This is set to v6.0.0 for new networks. + #[pallet::storage] + pub(crate) type StorageVersion = StorageValue<_, Releases, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub history_depth: u32, + pub validator_count: u32, + pub minimum_validator_count: u32, + pub invulnerables: Vec, + pub force_era: Forcing, + pub slash_reward_fraction: Perbill, + pub canceled_payout: BalanceOf, + pub stakers: Vec<(T::AccountId, T::AccountId, BalanceOf, StakerStatus)>, + pub min_nominator_bond: BalanceOf, + pub min_validator_bond: BalanceOf, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + history_depth: 84u32, + validator_count: Default::default(), + minimum_validator_count: Default::default(), + invulnerables: Default::default(), + force_era: Default::default(), + slash_reward_fraction: Default::default(), + canceled_payout: Default::default(), + stakers: Default::default(), + min_nominator_bond: Default::default(), + min_validator_bond: Default::default(), + } + } } - add_extra_genesis { - config(stakers): - Vec<(T::AccountId, T::AccountId, BalanceOf, StakerStatus)>; - build(|config: &GenesisConfig| { - for &(ref stash, ref controller, balance, ref status) in &config.stakers { + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + HistoryDepth::::put(self.history_depth); + ValidatorCount::::put(self.validator_count); + MinimumValidatorCount::::put(self.minimum_validator_count); + Invulnerables::::put(&self.invulnerables); + ForceEra::::put(self.force_era); + CanceledSlashPayout::::put(self.canceled_payout); + SlashRewardFraction::::put(self.slash_reward_fraction); + StorageVersion::::put(Releases::V6_0_0); + MinNominatorBond::::put(self.min_nominator_bond); + MinValidatorBond::::put(self.min_validator_bond); + + for &(ref stash, ref controller, balance, ref status) in &self.stakers { assert!( T::Currency::free_balance(&stash) >= balance, "Stash does not have enough balance to bond." ); - let _ = >::bond( + let _ = >::bond( T::Origin::from(Some(stash.clone()).into()), T::Lookup::unlookup(controller.clone()), balance, @@ -1014,95 +1277,35 @@ decl_storage! { ); let _ = match status { StakerStatus::Validator => { - >::validate( + >::validate( T::Origin::from(Some(controller.clone()).into()), Default::default(), ) }, StakerStatus::Nominator(votes) => { - >::nominate( + >::nominate( T::Origin::from(Some(controller.clone()).into()), votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(), ) }, _ => Ok(()) }; } - }); - } -} - -pub mod migrations { - use super::*; - - pub mod v6 { - use super::*; - use frame_support::{traits::Get, weights::Weight, pallet_prelude::*}; - - macro_rules! generate_storage_types { - ($name:ident => Value<$value:ty>) => { - paste::paste! { - struct [<$name Instance>]; - impl frame_support::traits::StorageInstance for [<$name Instance>] { - fn pallet_prefix() -> &'static str { - "Staking" - } - const STORAGE_PREFIX: &'static str = stringify!($name); - } - type $name = StorageValue<[<$name Instance>], $value, ValueQuery>; - } - } - } - - // NOTE: value type doesn't matter, we just set it to () here. - generate_storage_types!(SnapshotValidators => Value<()>); - generate_storage_types!(SnapshotNominators => Value<()>); - generate_storage_types!(QueuedElected => Value<()>); - generate_storage_types!(QueuedScore => Value<()>); - generate_storage_types!(EraElectionStatus => Value<()>); - generate_storage_types!(IsCurrentSessionFinal => Value<()>); - - /// check to execute prior to migration. - pub fn pre_migrate() -> Result<(), &'static str> { - // these may or may not exist. - log!(info, "SnapshotValidators.exits()? {:?}", SnapshotValidators::exists()); - log!(info, "SnapshotNominators.exits()? {:?}", SnapshotNominators::exists()); - log!(info, "QueuedElected.exits()? {:?}", QueuedElected::exists()); - log!(info, "QueuedScore.exits()? {:?}", QueuedScore::exists()); - // these must exist. - assert!(IsCurrentSessionFinal::exists(), "IsCurrentSessionFinal storage item not found!"); - assert!(EraElectionStatus::exists(), "EraElectionStatus storage item not found!"); - Ok(()) - } - - /// Migrate storage to v6. - pub fn migrate() -> Weight { - log!(info, "Migrating staking to Releases::V6_0_0"); - - SnapshotValidators::kill(); - SnapshotNominators::kill(); - QueuedElected::kill(); - QueuedScore::kill(); - EraElectionStatus::kill(); - IsCurrentSessionFinal::kill(); - - StorageVersion::put(Releases::V6_0_0); - log!(info, "Done."); - T::DbWeight::get().writes(6 + 1) } } -} -decl_event!( - pub enum Event where Balance = BalanceOf, ::AccountId { + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::metadata(T::AccountId = "AccountId", BalanceOf = "Balance")] + pub enum Event { /// The era payout has been set; the first balance is the validator-payout; the second is /// the remainder from the maximum amount of reward. /// \[era_index, validator_payout, remainder\] - EraPayout(EraIndex, Balance, Balance), + EraPayout(EraIndex, BalanceOf, BalanceOf), /// The staker has been rewarded by this amount. \[stash, amount\] - Reward(AccountId, Balance), + Reward(T::AccountId, BalanceOf), /// One validator (and its nominators) has been slashed by the given amount. /// \[validator, amount\] - Slash(AccountId, Balance), + Slash(T::AccountId, BalanceOf), /// An old slashing report from a prior era was discarded because it could /// not be processed. \[session_index\] OldSlashingReportDiscarded(SessionIndex), @@ -1112,20 +1315,20 @@ decl_event!( /// /// NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably, /// it will not be emitted for staking rewards when they are added to stake. - Bonded(AccountId, Balance), + Bonded(T::AccountId, BalanceOf), /// An account has unbonded this amount. \[stash, amount\] - Unbonded(AccountId, Balance), + Unbonded(T::AccountId, BalanceOf), /// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance` /// from the unlocking queue. \[stash, amount\] - Withdrawn(AccountId, Balance), + Withdrawn(T::AccountId, BalanceOf), /// A nominator has been kicked from a validator. \[nominator, stash\] - Kicked(AccountId, AccountId), + Kicked(T::AccountId, T::AccountId), + /// The election failed. No new era is planned. + StakingElectionFailed, } -); -decl_error! { - /// Error for the staking module. - pub enum Error for Module { + #[pallet::error] + pub enum Error { /// Not a controller account. NotController, /// Not a stash account. @@ -1140,8 +1343,8 @@ decl_error! { DuplicateIndex, /// Slash record index out of bounds. InvalidSlashIndex, - /// Can not bond with value less than minimum balance. - InsufficientValue, + /// Can not bond with value less than minimum required. + InsufficientBond, /// Can not schedule more unlock chunks. NoMoreChunks, /// Can not rebond without unlocking chunks. @@ -1166,69 +1369,69 @@ decl_error! { TooManyTargets, /// A nomination target was supplied that was blocked or otherwise not a validator. BadTarget, + /// The user has enough bond and thus cannot be chilled forcefully by an external person. + CannotChillOther, + /// There are too many nominators in the system. Governance needs to adjust the staking settings + /// to keep things safe for the runtime. + TooManyNominators, + /// There are too many validators in the system. Governance needs to adjust the staking settings + /// to keep things safe for the runtime. + TooManyValidators, } -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - /// Number of sessions per era. - const SessionsPerEra: SessionIndex = T::SessionsPerEra::get(); - - /// Number of eras that staked funds must remain bonded for. - const BondingDuration: EraIndex = T::BondingDuration::get(); - - /// Number of eras that slashes are deferred by, after computation. - /// - /// This should be less than the bonding duration. - /// Set to 0 if slashes should be applied immediately, without opportunity for - /// intervention. - const SlashDeferDuration: EraIndex = T::SlashDeferDuration::get(); - - /// The maximum number of nominators rewarded for each validator. - /// - /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim - /// their reward. This used to limit the i/o cost for the nominator payout. - const MaxNominatorRewardedPerValidator: u32 = T::MaxNominatorRewardedPerValidator::get(); - - /// Maximum number of nominations per nominator. - const MaxNominations: u32 = T::MAX_NOMINATIONS; - - type Error = Error; - - fn deposit_event() = default; + #[pallet::hooks] + impl Hooks> for Pallet { fn on_runtime_upgrade() -> Weight { - if StorageVersion::get() == Releases::V5_0_0 { - migrations::v6::migrate::() + if StorageVersion::::get() == Releases::V6_0_0 { + migrations::v7::migrate::() } else { T::DbWeight::get().reads(1) } } - fn on_finalize() { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<(), &'static str> { + if StorageVersion::::get() == Releases::V6_0_0 { + migrations::v7::pre_migrate::() + } else { + Ok(()) + } + } + + fn on_initialize(_now: BlockNumberFor) -> Weight { + // just return the weight of the on_finalize. + T::DbWeight::get().reads(1) + } + + fn on_finalize(_n: BlockNumberFor) { // Set the start of the first era. if let Some(mut active_era) = Self::active_era() { if active_era.start.is_none() { let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::(); active_era.start = Some(now_as_millis_u64); // This write only ever happens once, we don't include it in the weight in general - ActiveEra::put(active_era); + ActiveEra::::put(active_era); } } // `on_finalize` weight is tracked in `on_initialize` } fn integrity_test() { - sp_io::TestExternalities::new_empty().execute_with(|| - assert!( - T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0, - "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).", - T::SlashDeferDuration::get(), - T::BondingDuration::get(), - ) - ); + sp_std::if_std! { + sp_io::TestExternalities::new_empty().execute_with(|| + assert!( + T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0, + "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).", + T::SlashDeferDuration::get(), + T::BondingDuration::get(), + ) + ); + } } + } + #[pallet::call] + impl Pallet { /// Take the origin account as a stash and lock up `value` of its balance. `controller` will /// be the account that controls it. /// @@ -1251,12 +1454,13 @@ decl_module! { /// - Read: Bonded, Ledger, [Origin Account], Current Era, History Depth, Locks /// - Write: Bonded, Payee, [Origin Account], Locks, Ledger /// # - #[weight = T::WeightInfo::bond()] - pub fn bond(origin, + #[pallet::weight(T::WeightInfo::bond())] + pub fn bond( + origin: OriginFor, controller: ::Source, - #[compact] value: BalanceOf, + #[pallet::compact] value: BalanceOf, payee: RewardDestination, - ) { + ) -> DispatchResult { let stash = ensure_signed(origin)?; if >::contains_key(&stash) { @@ -1269,25 +1473,25 @@ decl_module! { Err(Error::::AlreadyPaired)? } - // reject a bond which is considered to be _dust_. + // Reject a bond which is considered to be _dust_. if value < T::Currency::minimum_balance() { - Err(Error::::InsufficientValue)? + Err(Error::::InsufficientBond)? } - system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; + frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; // You're auto-bonded forever, here. We might improve this by only bonding when // you actually validate/nominate and remove once you unbond __everything__. >::insert(&stash, &controller); >::insert(&stash, payee); - let current_era = CurrentEra::get().unwrap_or(0); + let current_era = CurrentEra::::get().unwrap_or(0); let history_depth = Self::history_depth(); let last_reward_era = current_era.saturating_sub(history_depth); let stash_balance = T::Currency::free_balance(&stash); let value = value.min(stash_balance); - Self::deposit_event(RawEvent::Bonded(stash.clone(), value)); + Self::deposit_event(Event::::Bonded(stash.clone(), value)); let item = StakingLedger { stash, total: value, @@ -1296,6 +1500,7 @@ decl_module! { claimed_rewards: (last_reward_era..current_era).collect(), }; Self::update_ledger(&controller, &item); + Ok(()) } /// Add some extra amount that have appeared in the stash `free_balance` into the balance up @@ -1319,8 +1524,11 @@ decl_module! { /// - Read: Era Election Status, Bonded, Ledger, [Origin Account], Locks /// - Write: [Origin Account], Locks, Ledger /// # - #[weight = T::WeightInfo::bond_extra()] - fn bond_extra(origin, #[compact] max_additional: BalanceOf) { + #[pallet::weight(T::WeightInfo::bond_extra())] + pub fn bond_extra( + origin: OriginFor, + #[pallet::compact] max_additional: BalanceOf, + ) -> DispatchResult { let stash = ensure_signed(origin)?; let controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; @@ -1331,12 +1539,13 @@ decl_module! { let extra = extra.min(max_additional); ledger.total += extra; ledger.active += extra; - // last check: the new active amount of ledger must be more than ED. - ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientValue); + // Last check: the new active amount of ledger must be more than ED. + ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientBond); - Self::deposit_event(RawEvent::Bonded(stash, extra)); + Self::deposit_event(Event::::Bonded(stash, extra)); Self::update_ledger(&controller, &ledger); } + Ok(()) } /// Schedule a portion of the stash to be unlocked ready for transfer out after the bond @@ -1350,6 +1559,9 @@ decl_module! { /// can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need /// to be called first to remove some of the chunks (if possible). /// + /// If a user encounters the `InsufficientBond` error when calling this extrinsic, + /// they should call `chill` first in order to free up their bonded funds. + /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. /// @@ -1371,8 +1583,8 @@ decl_module! { /// - Read: EraElectionStatus, Ledger, CurrentEra, Locks, BalanceOf Stash, /// - Write: Locks, Ledger, BalanceOf Stash, /// - #[weight = T::WeightInfo::unbond()] - fn unbond(origin, #[compact] value: BalanceOf) { + #[pallet::weight(T::WeightInfo::unbond())] + pub fn unbond(origin: OriginFor, #[pallet::compact] value: BalanceOf) -> DispatchResult { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; ensure!( @@ -1391,12 +1603,25 @@ decl_module! { ledger.active = Zero::zero(); } + let min_active_bond = if Nominators::::contains_key(&ledger.stash) { + MinNominatorBond::::get() + } else if Validators::::contains_key(&ledger.stash) { + MinValidatorBond::::get() + } else { + Zero::zero() + }; + + // Make sure that the user maintains enough active bond for their role. + // If a user runs into this error, they should chill first. + ensure!(ledger.active >= min_active_bond, Error::::InsufficientBond); + // Note: in case there is no current era it is fine to bond one era more. let era = Self::current_era().unwrap_or(0) + T::BondingDuration::get(); ledger.unlocking.push(UnlockChunk { value, era }); Self::update_ledger(&controller, &ledger); - Self::deposit_event(RawEvent::Unbonded(ledger.stash, value)); + Self::deposit_event(Event::::Unbonded(ledger.stash, value)); } + Ok(()) } /// Remove any unlocked chunks from the `unlocking` queue from our management. @@ -1430,8 +1655,11 @@ decl_module! { /// - Writes Each: SpanSlash * S /// NOTE: Weight annotation is the kill scenario, we refund otherwise. /// # - #[weight = T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans)] - fn withdraw_unbonded(origin, num_slashing_spans: u32) -> DispatchResultWithPostInfo { + #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans))] + pub fn withdraw_unbonded( + origin: OriginFor, + num_slashing_spans: u32, + ) -> DispatchResultWithPostInfo { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; let (stash, old_total) = (ledger.stash.clone(), ledger.total); @@ -1444,7 +1672,7 @@ decl_module! { // portion to fall below existential deposit + will have no more unlocking chunks // left. We can now safely remove all staking-related information. Self::kill_stash(&stash, num_slashing_spans)?; - // remove the lock. + // Remove the lock. T::Currency::remove_lock(STAKING_ID, &stash); // This is worst case scenario, so we use the full weight and return None None @@ -1461,7 +1689,7 @@ decl_module! { if ledger.total < old_total { // Already checked that this won't overflow by entry condition. let value = old_total - ledger.total; - Self::deposit_event(RawEvent::Withdrawn(stash, value)); + Self::deposit_event(Event::::Withdrawn(stash, value)); } Ok(post_info_weight.into()) @@ -1484,13 +1712,23 @@ decl_module! { /// - Read: Era Election Status, Ledger /// - Write: Nominators, Validators /// # - #[weight = T::WeightInfo::validate()] - pub fn validate(origin, prefs: ValidatorPrefs) { + #[pallet::weight(T::WeightInfo::validate())] + pub fn validate(origin: OriginFor, prefs: ValidatorPrefs) -> DispatchResult { let controller = ensure_signed(origin)?; + + // If this error is reached, we need to adjust the `MinValidatorBond` and start calling `chill_other`. + // Until then, we explicitly block new validators to protect the runtime. + if let Some(max_validators) = MaxValidatorsCount::::get() { + ensure!(CounterForValidators::::get() < max_validators, Error::::TooManyValidators); + } + let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; + ensure!(ledger.active >= MinValidatorBond::::get(), Error::::InsufficientBond); + let stash = &ledger.stash; - >::remove(stash); - >::insert(stash, prefs); + Self::do_remove_nominator(stash); + Self::do_add_validator(stash, prefs); + Ok(()) } /// Declare the desire to nominate `targets` for the origin controller. @@ -1512,10 +1750,22 @@ decl_module! { /// - Reads: Era Election Status, Ledger, Current Era /// - Writes: Validators, Nominators /// # - #[weight = T::WeightInfo::nominate(targets.len() as u32)] - pub fn nominate(origin, targets: Vec<::Source>) { + #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))] + pub fn nominate( + origin: OriginFor, + targets: Vec<::Source>, + ) -> DispatchResult { let controller = ensure_signed(origin)?; + + // If this error is reached, we need to adjust the `MinNominatorBond` and start calling `chill_other`. + // Until then, we explicitly block new nominators to protect the runtime. + if let Some(max_nominators) = MaxNominatorsCount::::get() { + ensure!(CounterForNominators::::get() < max_nominators, Error::::TooManyNominators); + } + let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; + ensure!(ledger.active >= MinNominatorBond::::get(), Error::::InsufficientBond); + let stash = &ledger.stash; ensure!(!targets.is_empty(), Error::::EmptyTargets); ensure!(targets.len() <= T::MAX_NOMINATIONS as usize, Error::::TooManyTargets); @@ -1533,13 +1783,14 @@ decl_module! { let nominations = Nominations { targets, - // initial nominations are considered submitted at era 0. See `Nominations` doc + // Initial nominations are considered submitted at era 0. See `Nominations` doc submitted_in: Self::current_era().unwrap_or(0), suppressed: false, }; - >::remove(stash); - >::insert(stash, &nominations); + Self::do_remove_validator(stash); + Self::do_add_nominator(stash, nominations); + Ok(()) } /// Declare no desire to either validate or nominate. @@ -1559,11 +1810,12 @@ decl_module! { /// - Read: EraElectionStatus, Ledger /// - Write: Validators, Nominators /// # - #[weight = T::WeightInfo::chill()] - fn chill(origin) { + #[pallet::weight(T::WeightInfo::chill())] + pub fn chill(origin: OriginFor) -> DispatchResult { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; Self::chill_stash(&ledger.stash); + Ok(()) } /// (Re-)set the payment target for a controller. @@ -1582,12 +1834,16 @@ decl_module! { /// - Read: Ledger /// - Write: Payee /// # - #[weight = T::WeightInfo::set_payee()] - fn set_payee(origin, payee: RewardDestination) { + #[pallet::weight(T::WeightInfo::set_payee())] + pub fn set_payee( + origin: OriginFor, + payee: RewardDestination, + ) -> DispatchResult { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; let stash = &ledger.stash; >::insert(stash, payee); + Ok(()) } /// (Re-)set the controller of a stash. @@ -1606,8 +1862,11 @@ decl_module! { /// - Read: Bonded, Ledger New Controller, Ledger Old Controller /// - Write: Bonded, Ledger New Controller, Ledger Old Controller /// # - #[weight = T::WeightInfo::set_controller()] - fn set_controller(origin, controller: ::Source) { + #[pallet::weight(T::WeightInfo::set_controller())] + pub fn set_controller( + origin: OriginFor, + controller: ::Source, + ) -> DispatchResult { let stash = ensure_signed(origin)?; let old_controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; let controller = T::Lookup::lookup(controller)?; @@ -1620,6 +1879,7 @@ decl_module! { >::insert(&controller, l); } } + Ok(()) } /// Sets the ideal number of validators. @@ -1630,10 +1890,14 @@ decl_module! { /// Weight: O(1) /// Write: Validator Count /// # - #[weight = T::WeightInfo::set_validator_count()] - fn set_validator_count(origin, #[compact] new: u32) { + #[pallet::weight(T::WeightInfo::set_validator_count())] + pub fn set_validator_count( + origin: OriginFor, + #[pallet::compact] new: u32, + ) -> DispatchResult { ensure_root(origin)?; - ValidatorCount::put(new); + ValidatorCount::::put(new); + Ok(()) } /// Increments the ideal number of validators. @@ -1643,10 +1907,14 @@ decl_module! { /// # /// Same as [`set_validator_count`]. /// # - #[weight = T::WeightInfo::set_validator_count()] - fn increase_validator_count(origin, #[compact] additional: u32) { + #[pallet::weight(T::WeightInfo::set_validator_count())] + pub fn increase_validator_count( + origin: OriginFor, + #[pallet::compact] additional: u32, + ) -> DispatchResult { ensure_root(origin)?; - ValidatorCount::mutate(|n| *n += additional); + ValidatorCount::::mutate(|n| *n += additional); + Ok(()) } /// Scale up the ideal number of validators by a factor. @@ -1656,25 +1924,33 @@ decl_module! { /// # /// Same as [`set_validator_count`]. /// # - #[weight = T::WeightInfo::set_validator_count()] - fn scale_validator_count(origin, factor: Percent) { + #[pallet::weight(T::WeightInfo::set_validator_count())] + pub fn scale_validator_count(origin: OriginFor, factor: Percent) -> DispatchResult { ensure_root(origin)?; - ValidatorCount::mutate(|n| *n += factor * *n); + ValidatorCount::::mutate(|n| *n += factor * *n); + Ok(()) } /// Force there to be no new eras indefinitely. /// /// The dispatch origin must be Root. /// + /// # Warning + /// + /// The election process starts multiple blocks before the end of the era. + /// Thus the election process may be ongoing when this is called. In this case the + /// election will continue until the next era is triggered. + /// /// # /// - No arguments. /// - Weight: O(1) /// - Write: ForceEra /// # - #[weight = T::WeightInfo::force_no_eras()] - fn force_no_eras(origin) { + #[pallet::weight(T::WeightInfo::force_no_eras())] + pub fn force_no_eras(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::put(Forcing::ForceNone); + ForceEra::::put(Forcing::ForceNone); + Ok(()) } /// Force there to be a new era at the end of the next session. After this, it will be @@ -1682,15 +1958,22 @@ decl_module! { /// /// The dispatch origin must be Root. /// + /// # Warning + /// + /// The election process starts multiple blocks before the end of the era. + /// If this is called just before a new era is triggered, the election process may not + /// have enough blocks to get a result. + /// /// # /// - No arguments. /// - Weight: O(1) /// - Write ForceEra /// # - #[weight = T::WeightInfo::force_new_era()] - fn force_new_era(origin) { + #[pallet::weight(T::WeightInfo::force_new_era())] + pub fn force_new_era(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::put(Forcing::ForceNew); + ForceEra::::put(Forcing::ForceNew); + Ok(()) } /// Set the validators who cannot be slashed (if any). @@ -1701,10 +1984,14 @@ decl_module! { /// - O(V) /// - Write: Invulnerables /// # - #[weight = T::WeightInfo::set_invulnerables(invulnerables.len() as u32)] - fn set_invulnerables(origin, invulnerables: Vec) { + #[pallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))] + pub fn set_invulnerables( + origin: OriginFor, + invulnerables: Vec, + ) -> DispatchResult { ensure_root(origin)?; >::put(invulnerables); + Ok(()) } /// Force a current staker to become completely unstaked, immediately. @@ -1717,29 +2004,41 @@ decl_module! { /// Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Account, Locks /// Writes Each: SpanSlash * S /// # - #[weight = T::WeightInfo::force_unstake(*num_slashing_spans)] - fn force_unstake(origin, stash: T::AccountId, num_slashing_spans: u32) { + #[pallet::weight(T::WeightInfo::force_unstake(*num_slashing_spans))] + pub fn force_unstake( + origin: OriginFor, + stash: T::AccountId, + num_slashing_spans: u32, + ) -> DispatchResult { ensure_root(origin)?; - // remove all staking-related information. + // Remove all staking-related information. Self::kill_stash(&stash, num_slashing_spans)?; - // remove the lock. + // Remove the lock. T::Currency::remove_lock(STAKING_ID, &stash); + Ok(()) } /// Force there to be a new era at the end of sessions indefinitely. /// /// The dispatch origin must be Root. /// + /// # Warning + /// + /// The election process starts multiple blocks before the end of the era. + /// If this is called just before a new era is triggered, the election process may not + /// have enough blocks to get a result. + /// /// # /// - Weight: O(1) /// - Write: ForceEra /// # - #[weight = T::WeightInfo::force_new_era_always()] - fn force_new_era_always(origin) { + #[pallet::weight(T::WeightInfo::force_new_era_always())] + pub fn force_new_era_always(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::put(Forcing::ForceAlways); + ForceEra::::put(Forcing::ForceAlways); + Ok(()) } /// Cancel enactment of a deferred slash. @@ -1755,8 +2054,12 @@ decl_module! { /// - Read: Unapplied Slashes /// - Write: Unapplied Slashes /// # - #[weight = T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32)] - fn cancel_deferred_slash(origin, era: EraIndex, slash_indices: Vec) { + #[pallet::weight(T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32))] + pub fn cancel_deferred_slash( + origin: OriginFor, + era: EraIndex, + slash_indices: Vec, + ) -> DispatchResult { T::SlashCancelOrigin::ensure_origin(origin)?; ensure!(!slash_indices.is_empty(), Error::::EmptyTargets); @@ -1772,6 +2075,7 @@ decl_module! { } ::UnappliedSlashes::insert(&era, &unapplied); + Ok(()) } /// Pay out all the stakers behind a single validator for a single era. @@ -1802,8 +2106,12 @@ decl_module! { /// NOTE: weights are assuming that payouts are made to alive stash account (Staked). /// Paying even a dead controller is cheaper weight-wise. We don't do any refunds here. /// # - #[weight = T::WeightInfo::payout_stakers_alive_staked(T::MaxNominatorRewardedPerValidator::get())] - fn payout_stakers(origin, validator_stash: T::AccountId, era: EraIndex) -> DispatchResult { + #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxNominatorRewardedPerValidator::get()))] + pub(super) fn payout_stakers( + origin: OriginFor, + validator_stash: T::AccountId, + era: EraIndex, + ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; Self::do_payout_stakers(validator_stash, era) } @@ -1822,16 +2130,20 @@ decl_module! { /// - Reads: EraElectionStatus, Ledger, Locks, [Origin Account] /// - Writes: [Origin Account], Locks, Ledger /// # - #[weight = T::WeightInfo::rebond(MAX_UNLOCKING_CHUNKS as u32)] - fn rebond(origin, #[compact] value: BalanceOf) -> DispatchResultWithPostInfo { + #[pallet::weight(T::WeightInfo::rebond(MAX_UNLOCKING_CHUNKS as u32))] + pub fn rebond( + origin: OriginFor, + #[pallet::compact] value: BalanceOf, + ) -> DispatchResultWithPostInfo { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; ensure!(!ledger.unlocking.is_empty(), Error::::NoUnlockChunk); let ledger = ledger.rebond(value); - // last check: the new active amount of ledger must be more than ED. - ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientValue); + // Last check: the new active amount of ledger must be more than ED. + ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientBond); + Self::deposit_event(Event::::Bonded(ledger.stash.clone(), value)); Self::update_ledger(&controller, &ledger); Ok(Some( 35 * WEIGHT_PER_MICROS @@ -1861,14 +2173,14 @@ decl_module! { /// - Clear Prefix Each: Era Stakers, EraStakersClipped, ErasValidatorPrefs /// - Writes Each: ErasValidatorReward, ErasRewardPoints, ErasTotalStake, ErasStartSessionIndex /// # - #[weight = T::WeightInfo::set_history_depth(*_era_items_deleted)] - fn set_history_depth(origin, - #[compact] new_history_depth: EraIndex, - #[compact] _era_items_deleted: u32, - ) { + #[pallet::weight(T::WeightInfo::set_history_depth(*_era_items_deleted))] + pub fn set_history_depth(origin: OriginFor, + #[pallet::compact] new_history_depth: EraIndex, + #[pallet::compact] _era_items_deleted: u32, + ) -> DispatchResult { ensure_root(origin)?; if let Some(current_era) = Self::current_era() { - HistoryDepth::mutate(|history_depth| { + HistoryDepth::::mutate(|history_depth| { let last_kept = current_era.checked_sub(*history_depth).unwrap_or(0); let new_last_kept = current_era.checked_sub(new_history_depth).unwrap_or(0); for era_index in last_kept..new_last_kept { @@ -1877,6 +2189,7 @@ decl_module! { *history_depth = new_history_depth }) } + Ok(()) } /// Remove all data structure concerning a staker/stash once its balance is at the minimum. @@ -1894,12 +2207,17 @@ decl_module! { /// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Stash Account, Locks /// - Writes Each: SpanSlash * S /// # - #[weight = T::WeightInfo::reap_stash(*num_slashing_spans)] - fn reap_stash(_origin, stash: T::AccountId, num_slashing_spans: u32) { + #[pallet::weight(T::WeightInfo::reap_stash(*num_slashing_spans))] + pub fn reap_stash( + _origin: OriginFor, + stash: T::AccountId, + num_slashing_spans: u32, + ) -> DispatchResult { let at_minimum = T::Currency::total_balance(&stash) == T::Currency::minimum_balance(); ensure!(at_minimum, Error::::FundedTarget); Self::kill_stash(&stash, num_slashing_spans)?; T::Currency::remove_lock(STAKING_ID, &stash); + Ok(()) } /// Remove the given nominations from the calling validator. @@ -1915,8 +2233,8 @@ decl_module! { /// /// Note: Making this call only makes sense if you first set the validator preferences to /// block any further nominations. - #[weight = T::WeightInfo::kick(who.len() as u32)] - pub fn kick(origin, who: Vec<::Source>) -> DispatchResult { + #[pallet::weight(T::WeightInfo::kick(who.len() as u32))] + pub fn kick(origin: OriginFor, who: Vec<::Source>) -> DispatchResult { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; let stash = &ledger.stash; @@ -1929,17 +2247,91 @@ decl_module! { Nominators::::mutate(&nom_stash, |maybe_nom| if let Some(ref mut nom) = maybe_nom { if let Some(pos) = nom.targets.iter().position(|v| v == stash) { nom.targets.swap_remove(pos); - Self::deposit_event(RawEvent::Kicked(nom_stash.clone(), stash.clone())); + Self::deposit_event(Event::::Kicked(nom_stash.clone(), stash.clone())); } }); } Ok(()) } + + /// Update the various staking limits this pallet. + /// + /// * `min_nominator_bond`: The minimum active bond needed to be a nominator. + /// * `min_validator_bond`: The minimum active bond needed to be a validator. + /// * `max_nominator_count`: The max number of users who can be a nominator at once. + /// When set to `None`, no limit is enforced. + /// * `max_validator_count`: The max number of users who can be a validator at once. + /// When set to `None`, no limit is enforced. + /// + /// Origin must be Root to call this function. + /// + /// NOTE: Existing nominators and validators will not be affected by this update. + /// to kick people under the new limits, `chill_other` should be called. + #[pallet::weight(T::WeightInfo::update_staking_limits())] + pub fn update_staking_limits( + origin: OriginFor, + min_nominator_bond: BalanceOf, + min_validator_bond: BalanceOf, + max_nominator_count: Option, + max_validator_count: Option, + ) -> DispatchResult { + ensure_root(origin)?; + MinNominatorBond::::set(min_nominator_bond); + MinValidatorBond::::set(min_validator_bond); + MaxNominatorsCount::::set(max_nominator_count); + MaxValidatorsCount::::set(max_validator_count); + Ok(()) + } + + /// Declare a `controller` as having no desire to either validator or nominate. + /// + /// Effects will be felt at the beginning of the next era. + /// + /// The dispatch origin for this call must be _Signed_, but can be called by anyone. + /// + /// If the caller is the same as the controller being targeted, then no further checks + /// are enforced. However, this call can also be made by an third party user who witnesses + /// that this controller does not satisfy the minimum bond requirements to be in their role. + /// + /// This can be helpful if bond requirements are updated, and we need to remove old users + /// who do not satisfy these requirements. + /// + // TODO: Maybe we can deprecate `chill` in the future. + // https://github.com/paritytech/substrate/issues/9111 + #[pallet::weight(T::WeightInfo::chill_other())] + pub fn chill_other( + origin: OriginFor, + controller: T::AccountId, + ) -> DispatchResult { + // Anyone can call this function. + let caller = ensure_signed(origin)?; + let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; + let stash = ledger.stash; + + // If the caller is not the controller, we want to check that the minimum bond + // requirements are not satisfied, and thus we have reason to chill this user. + // + // Otherwise, if caller is the same as the controller, this is just like `chill`. + if caller != controller { + let min_active_bond = if Nominators::::contains_key(&stash) { + MinNominatorBond::::get() + } else if Validators::::contains_key(&stash) { + MinValidatorBond::::get() + } else { + Zero::zero() + }; + + ensure!(ledger.active < min_active_bond, Error::::CannotChillOther); + } + + Self::chill_stash(&stash); + Ok(()) + } } } -impl Module { +impl Pallet { /// The total balance that can be slashed from a stash account as of right now. pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf { // Weight note: consider making the stake accessible through stash. @@ -1959,7 +2351,7 @@ impl Module { /// This prevents call sites from repeatedly requesting `total_issuance` from backend. But it is /// important to be only used while the total issuance is not changing. pub fn slashable_balance_of_fn() -> Box VoteWeight> { - // NOTE: changing this to unboxed `impl Fn(..)` return type and the module will still + // NOTE: changing this to unboxed `impl Fn(..)` return type and the pallet will still // compile, while some types in mock fail to resolve. let issuance = T::Currency::total_issuance(); Box::new(move |who: &T::AccountId| -> VoteWeight { @@ -1967,24 +2359,35 @@ impl Module { }) } - fn do_payout_stakers(validator_stash: T::AccountId, era: EraIndex) -> DispatchResult { + fn do_payout_stakers(validator_stash: T::AccountId, era: EraIndex) -> DispatchResultWithPostInfo { // Validate input data - let current_era = CurrentEra::get().ok_or(Error::::InvalidEraToReward)?; - ensure!(era <= current_era, Error::::InvalidEraToReward); + let current_era = CurrentEra::::get().ok_or( + Error::::InvalidEraToReward.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + )?; let history_depth = Self::history_depth(); - ensure!(era >= current_era.saturating_sub(history_depth), Error::::InvalidEraToReward); + ensure!( + era <= current_era && era >= current_era.saturating_sub(history_depth), + Error::::InvalidEraToReward.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + ); // Note: if era has no reward to be claimed, era may be future. better not to update // `ledger.claimed_rewards` in this case. let era_payout = >::get(&era) - .ok_or_else(|| Error::::InvalidEraToReward)?; - - let controller = Self::bonded(&validator_stash).ok_or(Error::::NotStash)?; + .ok_or_else(|| + Error::::InvalidEraToReward + .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + )?; + + let controller = Self::bonded(&validator_stash).ok_or( + Error::::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + )?; let mut ledger = >::get(&controller).ok_or_else(|| Error::::NotController)?; ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth)); match ledger.claimed_rewards.binary_search(&era) { - Ok(_) => Err(Error::::AlreadyClaimed)?, + Ok(_) => Err( + Error::::AlreadyClaimed.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) + )?, Err(pos) => ledger.claimed_rewards.insert(pos, era), } @@ -2008,7 +2411,9 @@ impl Module { .unwrap_or_else(|| Zero::zero()); // Nothing to do if they have no reward points. - if validator_reward_points.is_zero() { return Ok(())} + if validator_reward_points.is_zero() { + return Ok(Some(T::WeightInfo::payout_stakers_alive_staked(0)).into()) + } // This is the fraction of the total reward that the validator and the // nominators will get. @@ -2038,9 +2443,13 @@ impl Module { &ledger.stash, validator_staking_payout + validator_commission_payout ) { - Self::deposit_event(RawEvent::Reward(ledger.stash, imbalance.peek())); + Self::deposit_event(Event::::Reward(ledger.stash, imbalance.peek())); } + // Track the number of payout ops to nominators. Note: `WeightInfo::payout_stakers_alive_staked` + // always assumes at least a validator is paid out, so we do not need to count their payout op. + let mut nominator_payout_count: u32 = 0; + // Lets now calculate how this is split to the nominators. // Reward only the clipped exposures. Note this is not necessarily sorted. for nominator in exposure.others.iter() { @@ -2052,11 +2461,14 @@ impl Module { let nominator_reward: BalanceOf = nominator_exposure_part * validator_leftover_payout; // We can now make nominator payout: if let Some(imbalance) = Self::make_payout(&nominator.who, nominator_reward) { - Self::deposit_event(RawEvent::Reward(nominator.who.clone(), imbalance.peek())); + // Note: this logic does not count payouts for `RewardDestination::None`. + nominator_payout_count += 1; + Self::deposit_event(Event::::Reward(nominator.who.clone(), imbalance.peek())); } } - Ok(()) + debug_assert!(nominator_payout_count <= T::MaxNominatorRewardedPerValidator::get()); + Ok(Some(T::WeightInfo::payout_stakers_alive_staked(nominator_payout_count)).into()) } /// Update the ledger for a controller. @@ -2077,8 +2489,8 @@ impl Module { /// Chill a stash account. fn chill_stash(stash: &T::AccountId) { - >::remove(stash); - >::remove(stash); + Self::do_remove_validator(stash); + Self::do_remove_nominator(stash); } /// Actually make a payment to a staker. This uses the currency's reward function @@ -2109,10 +2521,9 @@ impl Module { } /// Plan a new session potentially trigger a new era. - fn new_session(session_index: SessionIndex) -> Option> { + fn new_session(session_index: SessionIndex, is_genesis: bool) -> Option> { if let Some(current_era) = Self::current_era() { // Initial era has been set. - let current_era_start_session_index = Self::eras_start_session_index(current_era) .unwrap_or_else(|| { frame_support::print("Error: start_session_index must be set for current_era"); @@ -2122,26 +2533,33 @@ impl Module { let era_length = session_index.checked_sub(current_era_start_session_index) .unwrap_or(0); // Must never happen. - match ForceEra::get() { - // Will set to default again, which is `NotForcing`. - Forcing::ForceNew => ForceEra::kill(), - // Short circuit to `new_era`. + match ForceEra::::get() { + // Will be set to `NotForcing` again if a new era has been triggered. + Forcing::ForceNew => (), + // Short circuit to `try_trigger_new_era`. Forcing::ForceAlways => (), - // Only go to `new_era` if deadline reached. + // Only go to `try_trigger_new_era` if deadline reached. Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (), _ => { - // either `Forcing::ForceNone`, + // Either `Forcing::ForceNone`, // or `Forcing::NotForcing if era_length >= T::SessionsPerEra::get()`. return None }, } - // new era. - Self::new_era(session_index) + // New era. + let maybe_new_era_validators = Self::try_trigger_new_era(session_index, is_genesis); + if maybe_new_era_validators.is_some() + && matches!(ForceEra::::get(), Forcing::ForceNew) + { + ForceEra::::put(Forcing::NotForcing); + } + + maybe_new_era_validators } else { - // Set initial era + // Set initial era. log!(debug, "Starting the first era."); - Self::new_era(session_index) + Self::try_trigger_new_era(session_index, is_genesis) } } @@ -2182,7 +2600,7 @@ impl Module { /// * reset `active_era.start`, /// * update `BondedEras` and apply slashes. fn start_era(start_session: SessionIndex) { - let active_era = ActiveEra::mutate(|active_era| { + let active_era = ActiveEra::::mutate(|active_era| { let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0); *active_era = Some(ActiveEraInfo { index: new_index, @@ -2194,18 +2612,18 @@ impl Module { let bonding_duration = T::BondingDuration::get(); - BondedEras::mutate(|bonded| { + BondedEras::::mutate(|bonded| { bonded.push((active_era, start_session)); if active_era > bonding_duration { let first_kept = active_era - bonding_duration; - // prune out everything that's from before the first-kept index. + // Prune out everything that's from before the first-kept index. let n_to_prune = bonded.iter() .take_while(|&&(era_idx, _)| era_idx < first_kept) .count(); - // kill slashing metadata. + // Kill slashing metadata. for (pruned_era, _) in bonded.drain(..n_to_prune) { slashing::clear_era_metadata::(pruned_era); } @@ -2230,7 +2648,7 @@ impl Module { let issuance = T::Currency::total_issuance(); let (validator_payout, rest) = T::EraPayout::era_payout(staked, issuance, era_duration); - Self::deposit_event(RawEvent::EraPayout(active_era.index, validator_payout, rest)); + Self::deposit_event(Event::::EraPayout(active_era.index, validator_payout, rest)); // Set ending era reward. >::insert(&active_era.index, validator_payout); @@ -2238,77 +2656,105 @@ impl Module { } } - /// Plan a new era. Return the potential new staking set. - fn new_era(start_session_index: SessionIndex) -> Option> { + /// Plan a new era. + /// + /// * Bump the current era storage (which holds the latest planned era). + /// * Store start session index for the new planned era. + /// * Clean old era information. + /// * Store staking information for the new planned era + /// + /// Returns the new validator set. + pub fn trigger_new_era( + start_session_index: SessionIndex, + exposures: Vec<(T::AccountId, Exposure>)>, + ) -> Vec { // Increment or set current era. - let current_era = CurrentEra::mutate(|s| { + let new_planned_era = CurrentEra::::mutate(|s| { *s = Some(s.map(|s| s + 1).unwrap_or(0)); s.unwrap() }); - ErasStartSessionIndex::insert(¤t_era, &start_session_index); + ErasStartSessionIndex::::insert(&new_planned_era, &start_session_index); // Clean old era information. - if let Some(old_era) = current_era.checked_sub(Self::history_depth() + 1) { + if let Some(old_era) = new_planned_era.checked_sub(Self::history_depth() + 1) { Self::clear_era_information(old_era); } - // Set staking information for new era. - let maybe_new_validators = Self::enact_election(current_era); - - maybe_new_validators + // Set staking information for the new era. + Self::store_stakers_info(exposures, new_planned_era) } - /// Enact and process the election using the `ElectionProvider` type. + /// Potentially plan a new era. + /// + /// Get election result from `T::ElectionProvider`. + /// In case election result has more than [`MinimumValidatorCount`] validator trigger a new era. /// - /// This will also process the election, as noted in [`process_election`]. - fn enact_election(current_era: EraIndex) -> Option> { - T::ElectionProvider::elect() - .map_err(|e| { - log!(warn, "election provider failed due to {:?}", e) + /// In case a new era is planned, the new validator set is returned. + fn try_trigger_new_era(start_session_index: SessionIndex, is_genesis: bool) -> Option> { + let (election_result, weight) = if is_genesis { + T::GenesisElectionProvider::elect().map_err(|e| { + log!(warn, "genesis election provider failed due to {:?}", e); + Self::deposit_event(Event::StakingElectionFailed); }) - .and_then(|(res, weight)| { - >::register_extra_weight_unchecked( - weight, - frame_support::weights::DispatchClass::Mandatory, - ); - Self::process_election(res, current_era) + } else { + T::ElectionProvider::elect().map_err(|e| { + log!(warn, "election provider failed due to {:?}", e); + Self::deposit_event(Event::StakingElectionFailed); }) - .ok() - } + } + .ok()?; - /// Process the output of the election. - /// - /// This ensures enough validators have been elected, converts all supports to exposures and - /// writes them to the associated storage. - /// - /// Returns `Err(())` if less than [`MinimumValidatorCount`] validators have been elected, `Ok` - /// otherwise. - pub fn process_election( - flat_supports: frame_election_provider_support::Supports, - current_era: EraIndex, - ) -> Result, ()> { - let exposures = Self::collect_exposures(flat_supports); - let elected_stashes = exposures.iter().cloned().map(|(x, _)| x).collect::>(); + >::register_extra_weight_unchecked( + weight, + frame_support::weights::DispatchClass::Mandatory, + ); + + let exposures = Self::collect_exposures(election_result); - if (elected_stashes.len() as u32) < Self::minimum_validator_count().max(1) { + if (exposures.len() as u32) < Self::minimum_validator_count().max(1) { // Session will panic if we ever return an empty validator set, thus max(1) ^^. - if current_era > 0 { - log!( + match CurrentEra::::get() { + Some(current_era) if current_era > 0 => log!( warn, - "chain does not have enough staking candidates to operate for era {:?} ({} elected, minimum is {})", - current_era, - elected_stashes.len(), + "chain does not have enough staking candidates to operate for era {:?} ({} \ + elected, minimum is {})", + CurrentEra::::get().unwrap_or(0), + exposures.len(), Self::minimum_validator_count(), - ); + ), + None => { + // The initial era is allowed to have no exposures. + // In this case the SessionManager is expected to choose a sensible validator + // set. + // TODO: this should be simplified #8911 + CurrentEra::::put(0); + ErasStartSessionIndex::::insert(&0, &start_session_index); + }, + _ => () } - return Err(()); + + Self::deposit_event(Event::StakingElectionFailed); + return None } + Self::deposit_event(Event::StakingElection); + Some(Self::trigger_new_era(start_session_index, exposures)) + } + + /// Process the output of the election. + /// + /// Store staking information for the new planned era + pub fn store_stakers_info( + exposures: Vec<(T::AccountId, Exposure>)>, + new_planned_era: EraIndex, + ) -> Vec { + let elected_stashes = exposures.iter().cloned().map(|(x, _)| x).collect::>(); + // Populate stakers, exposures, and the snapshot of validator prefs. let mut total_stake: BalanceOf = Zero::zero(); exposures.into_iter().for_each(|(stash, exposure)| { total_stake = total_stake.saturating_add(exposure.total); - >::insert(current_era, &stash, &exposure); + >::insert(new_planned_era, &stash, &exposure); let mut exposure_clipped = exposure; let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize; @@ -2316,31 +2762,28 @@ impl Module { exposure_clipped.others.sort_by(|a, b| a.value.cmp(&b.value).reverse()); exposure_clipped.others.truncate(clipped_max_len); } - >::insert(¤t_era, &stash, exposure_clipped); + >::insert(&new_planned_era, &stash, exposure_clipped); }); // Insert current era staking information - >::insert(¤t_era, total_stake); + >::insert(&new_planned_era, total_stake); - // collect the pref of all winners + // Collect the pref of all winners. for stash in &elected_stashes { let pref = Self::validators(stash); - >::insert(¤t_era, stash, pref); + >::insert(&new_planned_era, stash, pref); } - // emit event - Self::deposit_event(RawEvent::StakingElection); - - if current_era > 0 { + if new_planned_era > 0 { log!( info, "new validator set of size {:?} has been processed for era {:?}", elected_stashes.len(), - current_era, + new_planned_era, ); } - Ok(elected_stashes) + elected_stashes } /// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a @@ -2356,7 +2799,7 @@ impl Module { supports .into_iter() .map(|(validator, support)| { - // build `struct exposure` from `support` + // Build `struct exposure` from `support`. let mut others = Vec::with_capacity(support.voters.len()); let mut own: BalanceOf = Zero::zero(); let mut total: BalanceOf = Zero::zero(); @@ -2395,10 +2838,10 @@ impl Module { >::remove(&controller); >::remove(stash); - >::remove(stash); - >::remove(stash); + Self::do_remove_validator(stash); + Self::do_remove_nominator(stash); - system::Pallet::::dec_consumers(stash); + frame_system::Pallet::::dec_consumers(stash); Ok(()) } @@ -2411,7 +2854,7 @@ impl Module { >::remove(era_index); >::remove(era_index); >::remove(era_index); - ErasStartSessionIndex::remove(era_index); + ErasStartSessionIndex::::remove(era_index); } /// Apply previously-unapplied slashes on the beginning of a new era, after a delay. @@ -2456,9 +2899,9 @@ impl Module { /// Ensures that at the end of the current session there will be a new era. fn ensure_new_era() { - match ForceEra::get() { + match ForceEra::::get() { Forcing::ForceAlways | Forcing::ForceNew => (), - _ => ForceEra::put(Forcing::ForceNew), + _ => ForceEra::::put(Forcing::ForceNew), } } @@ -2473,7 +2916,7 @@ impl Module { #[cfg(feature = "runtime-benchmarks")] pub fn set_slash_reward_fraction(fraction: Perbill) { - SlashRewardFraction::put(fraction); + SlashRewardFraction::::put(fraction); } /// Get all of the voters that are eligible for the npos election. @@ -2491,15 +2934,15 @@ impl Module { let mut all_voters = Vec::new(); for (validator, _) in >::iter() { - // append self vote + // Append self vote. let self_vote = (validator.clone(), weight_of(&validator), vec![validator.clone()]); all_voters.push(self_vote); } - // collect all slashing spans into a BTreeMap for further queries. + // Collect all slashing spans into a BTreeMap for further queries. let slashing_spans = >::iter().collect::>(); - for (nominator, nominations) in >::iter() { + for (nominator, nominations) in Nominators::::iter() { let Nominations { submitted_in, mut targets, suppressed: _ } = nominations; // Filter out nomination targets which were nominated before the most recent @@ -2510,20 +2953,63 @@ impl Module { .map_or(true, |spans| submitted_in >= spans.last_nonzero_slash()) }); - let vote_weight = weight_of(&nominator); - all_voters.push((nominator, vote_weight, targets)) + if !targets.is_empty() { + let vote_weight = weight_of(&nominator); + all_voters.push((nominator, vote_weight, targets)) + } } all_voters } + /// This is a very expensive function and result should be cached versus being called multiple times. pub fn get_npos_targets() -> Vec { - >::iter().map(|(v, _)| v).collect::>() + Validators::::iter().map(|(v, _)| v).collect::>() + } + + /// This function will add a nominator to the `Nominators` storage map, + /// and keep track of the `CounterForNominators`. + /// + /// If the nominator already exists, their nominations will be updated. + pub fn do_add_nominator(who: &T::AccountId, nominations: Nominations) { + if !Nominators::::contains_key(who) { + CounterForNominators::::mutate(|x| x.saturating_inc()) + } + Nominators::::insert(who, nominations); + } + + /// This function will remove a nominator from the `Nominators` storage map, + /// and keep track of the `CounterForNominators`. + pub fn do_remove_nominator(who: &T::AccountId) { + if Nominators::::contains_key(who) { + Nominators::::remove(who); + CounterForNominators::::mutate(|x| x.saturating_dec()); + } + } + + /// This function will add a validator to the `Validators` storage map, + /// and keep track of the `CounterForValidators`. + /// + /// If the validator already exists, their preferences will be updated. + pub fn do_add_validator(who: &T::AccountId, prefs: ValidatorPrefs) { + if !Validators::::contains_key(who) { + CounterForValidators::::mutate(|x| x.saturating_inc()) + } + Validators::::insert(who, prefs); + } + + /// This function will remove a validator from the `Validators` storage map, + /// and keep track of the `CounterForValidators`. + pub fn do_remove_validator(who: &T::AccountId) { + if Validators::::contains_key(who) { + Validators::::remove(who); + CounterForValidators::::mutate(|x| x.saturating_dec()); + } } } impl frame_election_provider_support::ElectionDataProvider - for Module + for Pallet { const MAXIMUM_VOTES_PER_VOTER: u32 = T::MAX_NOMINATIONS; fn desired_targets() -> data_provider::Result<(u32, Weight)> { @@ -2533,12 +3019,11 @@ impl frame_election_provider_support::ElectionDataProvider, ) -> data_provider::Result<(Vec<(T::AccountId, VoteWeight, Vec)>, Weight)> { - // NOTE: reading these counts already needs to iterate a lot of storage keys, but they get - // cached. This is okay for the case of `Ok(_)`, but bad for `Err(_)`, as the trait does not - // report weight in failures. - let nominator_count = >::iter().count(); - let validator_count = >::iter().count(); - let voter_count = nominator_count.saturating_add(validator_count); + let nominator_count = CounterForNominators::::get(); + let validator_count = CounterForValidators::::get(); + let voter_count = nominator_count.saturating_add(validator_count) as usize; + debug_assert!(>::iter().count() as u32 == CounterForNominators::::get()); + debug_assert!(>::iter().count() as u32 == CounterForValidators::::get()); if maybe_max_len.map_or(false, |max_len| voter_count > max_len) { return Err("Voter snapshot too big"); @@ -2546,15 +3031,15 @@ impl frame_election_provider_support::ElectionDataProvider>::iter().count(); let weight = T::WeightInfo::get_npos_voters( - nominator_count as u32, - validator_count as u32, + nominator_count, + validator_count, slashing_span_count as u32, ); Ok((Self::get_npos_voters(), weight)) } fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { - let target_count = >::iter().count(); + let target_count = CounterForValidators::::get() as usize; if maybe_max_len.map_or(false, |max_len| target_count > max_len) { return Err("Target snapshot too big"); @@ -2569,22 +3054,28 @@ impl frame_election_provider_support::ElectionDataProvider::get() { + Forcing::ForceNone => Bounded::max_value(), + Forcing::ForceNew | Forcing::ForceAlways => Zero::zero(), + Forcing::NotForcing if era_progress >= T::SessionsPerEra::get() => Zero::zero(), + Forcing::NotForcing => T::SessionsPerEra::get() + .saturating_sub(era_progress) + // One session is computed in this_session_end. + .saturating_sub(1) + .into(), + }; now.saturating_add( until_this_session_end.saturating_add(sessions_left.saturating_mul(session_length)), @@ -2601,7 +3092,7 @@ impl frame_election_provider_support::ElectionDataProvider = target_stake .and_then(|w| >::try_from(w).ok()) - .unwrap_or(T::Currency::minimum_balance() * 100u32.into()); + .unwrap_or(MinNominatorBond::::get() * 100u32.into()); >::insert(v.clone(), v.clone()); >::insert( v.clone(), @@ -2613,8 +3104,8 @@ impl frame_election_provider_support::ElectionDataProvider>::insert( - v, + Self::do_add_validator( + &v, ValidatorPrefs { commission: Perbill::zero(), blocked: false }, ); }); @@ -2634,8 +3125,8 @@ impl frame_election_provider_support::ElectionDataProvider>::insert( - v, + Self::do_add_nominator( + &v, Nominations { targets: t, submitted_in: 0, suppressed: false }, ); }); @@ -2647,24 +3138,29 @@ impl frame_election_provider_support::ElectionDataProvider pallet_session::SessionManager for Module { +impl pallet_session::SessionManager for Pallet { fn new_session(new_index: SessionIndex) -> Option> { - log!(trace, "planning new_session({})", new_index); - CurrentPlannedSession::put(new_index); - Self::new_session(new_index) + log!(trace, "planning new session {}", new_index); + CurrentPlannedSession::::put(new_index); + Self::new_session(new_index, false) + } + fn new_session_genesis(new_index: SessionIndex) -> Option> { + log!(trace, "planning new session {} at genesis", new_index); + CurrentPlannedSession::::put(new_index); + Self::new_session(new_index, true) } fn start_session(start_index: SessionIndex) { - log!(trace, "starting start_session({})", start_index); + log!(trace, "starting session {}", start_index); Self::start_session(start_index) } fn end_session(end_index: SessionIndex) { - log!(trace, "ending end_session({})", end_index); + log!(trace, "ending session {}", end_index); Self::end_session(end_index) } } impl historical::SessionManager>> - for Module + for Pallet { fn new_session( new_index: SessionIndex, @@ -2680,6 +3176,20 @@ impl historical::SessionManager Option>)>> { + >::new_session_genesis(new_index).map(|validators| { + let current_era = Self::current_era() + // Must be some as a new era has been created. + .unwrap_or(0); + + validators.into_iter().map(|v| { + let exposure = Self::eras_stakers(current_era, &v); + (v, exposure) + }).collect() + }) + } fn start_session(start_index: SessionIndex) { >::start_session(start_index) } @@ -2692,7 +3202,7 @@ impl historical::SessionManager pallet_authorship::EventHandler for Module +impl pallet_authorship::EventHandler for Pallet where T: Config + pallet_authorship::Config + pallet_session::Config, { @@ -2713,7 +3223,7 @@ pub struct StashOf(sp_std::marker::PhantomData); impl Convert> for StashOf { fn convert(controller: T::AccountId) -> Option { - >::ledger(&controller).map(|l| l.stash) + >::ledger(&controller).map(|l| l.stash) } } @@ -2728,18 +3238,15 @@ impl Convert for ExposureOf { fn convert(validator: T::AccountId) -> Option>> { - if let Some(active_era) = >::active_era() { - Some(>::eras_stakers(active_era.index, &validator)) - } else { - None - } + >::active_era() + .map(|active_era| >::eras_stakers(active_era.index, &validator)) } } /// This is intended to be used with `FilterHistoricalOffences`. impl OnOffenceHandler, Weight> - for Module + for Pallet where T: pallet_session::Config::AccountId>, T: pallet_session::historical::Config< @@ -2760,12 +3267,8 @@ where >], slash_fraction: &[Perbill], slash_session: SessionIndex, - ) -> Result { - if !Self::can_report() { - return Err(()); - } - - let reward_proportion = SlashRewardFraction::get(); + ) -> Weight { + let reward_proportion = SlashRewardFraction::::get(); let mut consumed_weight: Weight = 0; let mut add_db_reads_writes = |reads, writes| { consumed_weight += T::DbWeight::get().reads_writes(reads, writes); @@ -2775,8 +3278,8 @@ where let active_era = Self::active_era(); add_db_reads_writes(1, 0); if active_era.is_none() { - // this offence need not be re-submitted. - return Ok(consumed_weight) + // This offence need not be re-submitted. + return consumed_weight } active_era.expect("value checked not to be `None`; qed").index }; @@ -2789,19 +3292,19 @@ where let window_start = active_era.saturating_sub(T::BondingDuration::get()); - // fast path for active-era report - most likely. + // Fast path for active-era report - most likely. // `slash_session` cannot be in a future active era. It must be in `active_era` or before. let slash_era = if slash_session >= active_era_start_session_index { active_era } else { - let eras = BondedEras::get(); + let eras = BondedEras::::get(); add_db_reads_writes(1, 0); - // reverse because it's more likely to find reports from recent eras. + // Reverse because it's more likely to find reports from recent eras. match eras.iter().rev().filter(|&&(_, ref sesh)| sesh <= &slash_session).next() { Some(&(ref slash_era, _)) => *slash_era, - // before bonding period. defensive - should be filtered out. - None => return Ok(consumed_weight), + // Before bonding period. defensive - should be filtered out. + None => return consumed_weight, } }; @@ -2846,7 +3349,7 @@ where } unapplied.reporters = details.reporters.clone(); if slash_defer_duration == 0 { - // apply right away. + // Apply right away. slashing::apply_slash::(unapplied); { let slash_cost = (6, 5); @@ -2857,7 +3360,7 @@ where ); } } else { - // defer to end of some `slash_defer_duration` from now. + // Defer to end of some `slash_defer_duration` from now. ::UnappliedSlashes::mutate( active_era, move |for_later| for_later.push(unapplied), @@ -2869,12 +3372,7 @@ where } } - Ok(consumed_weight) - } - - fn can_report() -> bool { - // TODO: https://github.com/paritytech/substrate/issues/8343 - true + consumed_weight } } @@ -2884,22 +3382,22 @@ pub struct FilterHistoricalOffences { } impl ReportOffence - for FilterHistoricalOffences, R> + for FilterHistoricalOffences, R> where T: Config, R: ReportOffence, O: Offence, { fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { - // disallow any slashing from before the current bonding period. + // Disallow any slashing from before the current bonding period. let offence_session = offence.session_index(); - let bonded_eras = BondedEras::get(); + let bonded_eras = BondedEras::::get(); if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { R::report_offence(reporters, offence) } else { - >::deposit_event( - RawEvent::OldSlashingReportDiscarded(offence_session) + >::deposit_event( + Event::::OldSlashingReportDiscarded(offence_session) ); Ok(()) } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 03f5acfad7286..dd961799a1408 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -21,9 +21,8 @@ use crate::*; use crate as staking; use frame_support::{ assert_ok, parameter_types, - traits::{Currency, FindAuthor, Get, OnFinalize, OnInitialize, OneSessionHandler}, + traits::{Currency, FindAuthor, Get, OnInitialize, OneSessionHandler}, weights::constants::RocksDbWeight, - IterableStorageMap, StorageDoubleMap, StorageMap, StorageValue, }; use sp_core::H256; use sp_io; @@ -190,7 +189,7 @@ impl pallet_authorship::Config for Test { type FindAuthor = Author11; type UncleGenerations = UncleGenerations; type FilterUncle = (); - type EventHandler = Module; + type EventHandler = Pallet; } parameter_types! { pub const MinimumPeriod: u64 = 5; @@ -239,6 +238,7 @@ impl onchain::Config for Test { type Accuracy = Perbill; type DataProvider = Staking; } + impl Config for Test { const MAX_NOMINATIONS: u32 = 16; type Currency = Balances; @@ -257,6 +257,7 @@ impl Config for Test { type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type ElectionProvider = onchain::OnChainSequentialPhragmen; + type GenesisElectionProvider = Self::ElectionProvider; type WeightInfo = (); } @@ -269,6 +270,8 @@ where } pub type Extrinsic = TestXt; +pub(crate) type StakingCall = crate::Call; +pub(crate) type TestRuntimeCall = ::Call; pub struct ExtBuilder { validator_pool: bool, @@ -280,6 +283,8 @@ pub struct ExtBuilder { invulnerables: Vec, has_stakers: bool, initialize_first_session: bool, + min_nominator_bond: Balance, + min_validator_bond: Balance, } impl Default for ExtBuilder { @@ -294,6 +299,8 @@ impl Default for ExtBuilder { invulnerables: vec![], has_stakers: true, initialize_first_session: true, + min_nominator_bond: ExistentialDeposit::get(), + min_validator_bond: ExistentialDeposit::get(), } } } @@ -355,7 +362,15 @@ impl ExtBuilder { OFFSET.with(|v| *v.borrow_mut() = offset); self } - pub fn build(self) -> sp_io::TestExternalities { + pub fn min_nominator_bond(mut self, amount: Balance) -> Self { + self.min_nominator_bond = amount; + self + } + pub fn min_validator_bond(mut self, amount: Balance) -> Self { + self.min_validator_bond = amount; + self + } + fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::default() .build_storage::() @@ -428,6 +443,8 @@ impl ExtBuilder { minimum_validator_count: self.minimum_validator_count, invulnerables: self.invulnerables, slash_reward_fraction: Perbill::from_percent(10), + min_nominator_bond: self.min_nominator_bond, + min_validator_bond: self.min_validator_bond, ..Default::default() } .assimilate_storage(&mut storage); @@ -453,7 +470,7 @@ impl ExtBuilder { ext.execute_with(|| { System::set_block_number(1); Session::on_initialize(1); - Staking::on_initialize(1); + >::on_initialize(1); Timestamp::set_timestamp(INIT_TIMESTAMP); }); } @@ -471,6 +488,14 @@ fn post_conditions() { check_nominators(); check_exposures(); check_ledgers(); + check_count(); +} + +fn check_count() { + let nominator_count = Nominators::::iter().count() as u32; + let validator_count = Validators::::iter().count() as u32; + assert_eq!(nominator_count, CounterForNominators::::get()); + assert_eq!(validator_count, CounterForValidators::::get()); } fn check_ledgers() { @@ -604,7 +629,7 @@ pub(crate) fn run_to_block(n: BlockNumber) { for b in (System::block_number() + 1)..=n { System::set_block_number(b); Session::on_initialize(b); - Staking::on_initialize(b); + >::on_initialize(b); Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); if b != n { Staking::on_finalize(System::block_number()); @@ -690,7 +715,7 @@ pub(crate) fn reward_all_elected() { .into_iter() .map(|v| (v, 1)); - >::reward_by_ids(rewards) + >::reward_by_ids(rewards) } pub(crate) fn validator_controllers() -> Vec { @@ -708,10 +733,10 @@ pub(crate) fn on_offence_in_era( slash_fraction: &[Perbill], era: EraIndex, ) { - let bonded_eras = crate::BondedEras::get(); + let bonded_eras = crate::BondedEras::::get(); for &(bonded_era, start_session) in bonded_eras.iter() { if bonded_era == era { - let _ = Staking::on_offence(offenders, slash_fraction, start_session).unwrap(); + let _ = Staking::on_offence(offenders, slash_fraction, start_session); return; } else if bonded_era > era { break; @@ -724,7 +749,7 @@ pub(crate) fn on_offence_in_era( offenders, slash_fraction, Staking::eras_start_session_index(era).unwrap() - ).unwrap(); + ); } else { panic!("cannot slash in era {}", era); } diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index 2b2ac61356c47..15ddc13f238b6 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -50,12 +50,12 @@ //! Based on research at use super::{ - EraIndex, Config, Module, Store, BalanceOf, Exposure, Perbill, SessionInterface, + EraIndex, Config, Pallet, Store, BalanceOf, Exposure, Perbill, SessionInterface, NegativeImbalanceOf, UnappliedSlash, Error, }; use sp_runtime::{traits::{Zero, Saturating}, RuntimeDebug, DispatchResult}; use frame_support::{ - StorageMap, StorageDoubleMap, ensure, + ensure, traits::{Currency, OnUnbalanced, Imbalance}, }; use sp_std::vec::Vec; @@ -239,7 +239,7 @@ pub(crate) fn compute_slash(params: SlashParams) return None; } - let (prior_slash_p, _era_slash) = as Store>::ValidatorSlashInEra::get( + let (prior_slash_p, _era_slash) = as Store>::ValidatorSlashInEra::get( &slash_era, stash, ).unwrap_or((Perbill::zero(), Zero::zero())); @@ -247,7 +247,7 @@ pub(crate) fn compute_slash(params: SlashParams) // compare slash proportions rather than slash values to avoid issues due to rounding // error. if slash.deconstruct() > prior_slash_p.deconstruct() { - as Store>::ValidatorSlashInEra::insert( + as Store>::ValidatorSlashInEra::insert( &slash_era, stash, &(slash, own_slash), @@ -285,12 +285,12 @@ pub(crate) fn compute_slash(params: SlashParams) // chill the validator - it misbehaved in the current span and should // not continue in the next election. also end the slashing span. spans.end_span(now); - >::chill_stash(stash); + >::chill_stash(stash); // make sure to disable validator till the end of this session if T::SessionInterface::disable_validator(stash).unwrap_or(false) { // force a new era, to select a new validator set - >::ensure_new_era() + >::ensure_new_era() } } } @@ -325,12 +325,12 @@ fn kick_out_if_recent( if spans.era_span(params.slash_era).map(|s| s.index) == Some(spans.span_index()) { spans.end_span(params.now); - >::chill_stash(params.stash); + >::chill_stash(params.stash); // make sure to disable validator till the end of this session if T::SessionInterface::disable_validator(params.stash).unwrap_or(false) { // force a new era, to select a new validator set - >::ensure_new_era() + >::ensure_new_era() } } } @@ -367,14 +367,14 @@ fn slash_nominators( let own_slash_by_validator = slash * nominator.value; let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior); - let mut era_slash = as Store>::NominatorSlashInEra::get( + let mut era_slash = as Store>::NominatorSlashInEra::get( &slash_era, stash, ).unwrap_or_else(|| Zero::zero()); era_slash += own_slash_difference; - as Store>::NominatorSlashInEra::insert( + as Store>::NominatorSlashInEra::insert( &slash_era, stash, &era_slash, @@ -437,9 +437,9 @@ fn fetch_spans<'a, T: Config + 'a>( slash_of: &'a mut BalanceOf, reward_proportion: Perbill, ) -> InspectingSpans<'a, T> { - let spans = as Store>::SlashingSpans::get(stash).unwrap_or_else(|| { + let spans = as Store>::SlashingSpans::get(stash).unwrap_or_else(|| { let spans = SlashingSpans::new(window_start); - as Store>::SlashingSpans::insert(stash, &spans); + as Store>::SlashingSpans::insert(stash, &spans); spans }); @@ -488,7 +488,7 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> { ) -> Option { let target_span = self.era_span(slash_era)?; let span_slash_key = (self.stash.clone(), target_span.index); - let mut span_record = as Store>::SpanSlash::get(&span_slash_key); + let mut span_record = as Store>::SpanSlash::get(&span_slash_key); let mut changed = false; let reward = if span_record.slashed < slash { @@ -519,7 +519,7 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> { if changed { self.dirty = true; - as Store>::SpanSlash::insert(&span_slash_key, &span_record); + as Store>::SpanSlash::insert(&span_slash_key, &span_record); } Some(target_span.index) @@ -533,18 +533,18 @@ impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> { if let Some((start, end)) = self.spans.prune(self.window_start) { for span_index in start..end { - as Store>::SpanSlash::remove(&(self.stash.clone(), span_index)); + as Store>::SpanSlash::remove(&(self.stash.clone(), span_index)); } } - as Store>::SlashingSpans::insert(self.stash, &self.spans); + as Store>::SlashingSpans::insert(self.stash, &self.spans); } } /// Clear slashing metadata for an obsolete era. pub(crate) fn clear_era_metadata(obsolete_era: EraIndex) { - as Store>::ValidatorSlashInEra::remove_prefix(&obsolete_era); - as Store>::NominatorSlashInEra::remove_prefix(&obsolete_era); + as Store>::ValidatorSlashInEra::remove_prefix(&obsolete_era); + as Store>::NominatorSlashInEra::remove_prefix(&obsolete_era); } /// Clear slashing metadata for a dead account. @@ -552,14 +552,14 @@ pub(crate) fn clear_stash_metadata( stash: &T::AccountId, num_slashing_spans: u32, ) -> DispatchResult { - let spans = match as Store>::SlashingSpans::get(stash) { + let spans = match as Store>::SlashingSpans::get(stash) { None => return Ok(()), Some(s) => s, }; ensure!(num_slashing_spans as usize >= spans.iter().count(), Error::::IncorrectSlashingSpans); - as Store>::SlashingSpans::remove(stash); + as Store>::SlashingSpans::remove(stash); // kill slashing-span metadata for account. // @@ -567,7 +567,7 @@ pub(crate) fn clear_stash_metadata( // in that case, they may re-bond, but it would count again as span 0. Further ancient // slashes would slash into this new bond, since metadata has now been cleared. for span in spans.iter() { - as Store>::SpanSlash::remove(&(stash.clone(), span.index)); + as Store>::SpanSlash::remove(&(stash.clone(), span.index)); } Ok(()) @@ -582,12 +582,12 @@ pub fn do_slash( reward_payout: &mut BalanceOf, slashed_imbalance: &mut NegativeImbalanceOf, ) { - let controller = match >::bonded(stash) { + let controller = match >::bonded(stash) { None => return, // defensive: should always exist. Some(c) => c, }; - let mut ledger = match >::ledger(&controller) { + let mut ledger = match >::ledger(&controller) { Some(ledger) => ledger, None => return, // nothing to do. }; @@ -603,11 +603,11 @@ pub fn do_slash( *reward_payout = reward_payout.saturating_sub(missing); } - >::update_ledger(&controller, &ledger); + >::update_ledger(&controller, &ledger); // trigger the event - >::deposit_event( - super::RawEvent::Slash(stash.clone(), value) + >::deposit_event( + super::Event::::Slash(stash.clone(), value) ); } } diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index c4daf88098e75..b3b8ef0843a7f 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -19,7 +19,7 @@ //! bonding validators, nominators, and generating different types of solutions. use crate::*; -use crate::Module as Staking; +use crate::Pallet as Staking; use frame_benchmarking::account; use frame_system::RawOrigin; use sp_io::hashing::blake2_256; @@ -30,7 +30,9 @@ const SEED: u32 = 0; /// This function removes all validators and nominators from storage. pub fn clear_validators_and_nominators() { Validators::::remove_all(); + CounterForValidators::::kill(); Nominators::::remove_all(); + CounterForNominators::::kill(); } /// Grab a funded user. @@ -166,12 +168,12 @@ pub fn create_validators_with_nominators_for_era( Staking::::nominate(RawOrigin::Signed(n_controller.clone()).into(), selected_validators)?; } - ValidatorCount::put(validators); + ValidatorCount::::put(validators); Ok(validator_chosen) } /// get the current era. pub fn current_era() -> EraIndex { - >::current_era().unwrap_or(0) + >::current_era().unwrap_or(0) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index df3456bf29926..31086ceff4233 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -17,15 +17,17 @@ //! Tests for the module. -use super::*; +use super::{*, Event}; use mock::*; use sp_runtime::{ - assert_eq_error_rate, traits::BadOrigin, + assert_eq_error_rate, + traits::{BadOrigin, Dispatchable}, }; use sp_staking::offence::OffenceDetails; use frame_support::{ - assert_ok, assert_noop, StorageMap, + assert_ok, assert_noop, traits::{Currency, ReservableCurrency, OnInitialize}, + weights::{extract_actual_weight, GetDispatchInfo}, }; use pallet_balances::Error as BalancesError; use substrate_test_utils::assert_eq_uvec; @@ -185,10 +187,10 @@ fn rewards_should_work() { Payee::::insert(21, RewardDestination::Controller); Payee::::insert(101, RewardDestination::Controller); - >::reward_by_ids(vec![(11, 50)]); - >::reward_by_ids(vec![(11, 50)]); + >::reward_by_ids(vec![(11, 50)]); + >::reward_by_ids(vec![(11, 50)]); // This is the second validator of the current elected set. - >::reward_by_ids(vec![(21, 50)]); + >::reward_by_ids(vec![(21, 50)]); // Compute total payout now for whole duration of the session. let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); @@ -225,7 +227,7 @@ fn rewards_should_work() { ); assert_eq!( *mock::staking_events().last().unwrap(), - RawEvent::EraPayout(0, total_payout_0, maximum_payout - total_payout_0) + Event::EraPayout(0, total_payout_0, maximum_payout - total_payout_0) ); mock::make_all_reward_payment(0); @@ -251,7 +253,7 @@ fn rewards_should_work() { assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2); assert_eq_uvec!(Session::validators(), vec![11, 21]); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); @@ -263,7 +265,7 @@ fn rewards_should_work() { ); assert_eq!( *mock::staking_events().last().unwrap(), - RawEvent::EraPayout(1, total_payout_1, maximum_payout - total_payout_1) + Event::EraPayout(1, total_payout_1, maximum_payout - total_payout_1) ); mock::make_all_reward_payment(1); @@ -295,8 +297,7 @@ fn staking_should_work() { ExtBuilder::default() .nominate(false) .fair(false) // to give 20 more staked value - .build() - .execute_with(|| { + .build_and_execute(|| { // remember + compare this along with the test. assert_eq_uvec!(validator_controllers(), vec![20, 10]); @@ -372,8 +373,7 @@ fn blocking_and_kicking_works() { .validator_count(4) .nominate(true) .num_validators(3) - .build() - .execute_with(|| { + .build_and_execute(|| { // block validator 10/11 assert_ok!(Staking::validate(Origin::signed(10), ValidatorPrefs { blocked: true, .. Default::default() })); // attempt to nominate from 100/101... @@ -396,8 +396,7 @@ fn less_than_needed_candidates_works() { .validator_count(4) .nominate(false) .num_validators(3) - .build() - .execute_with(|| { + .build_and_execute(|| { assert_eq!(Staking::validator_count(), 4); assert_eq!(Staking::minimum_validator_count(), 1); assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); @@ -424,8 +423,7 @@ fn no_candidate_emergency_condition() { .num_validators(4) .validator_pool(true) .nominate(false) - .build() - .execute_with(|| { + .build_and_execute(|| { // initial validators assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); let prefs = ValidatorPrefs { commission: Perbill::one(), .. Default::default() }; @@ -437,13 +435,26 @@ fn no_candidate_emergency_condition() { // try to chill let _ = Staking::chill(Origin::signed(10)); - // trigger era - mock::start_active_era(1); + let current_era = CurrentEra::::get(); + + // try trigger new era + mock::run_to_block(20); + assert_eq!( + *staking_events().last().unwrap(), + Event::StakingElectionFailed, + ); + // No new era is created + assert_eq!(current_era, CurrentEra::::get()); + + // Go to far further session to see if validator have changed + mock::run_to_block(100); - // Previous ones are elected. chill is invalidates. TODO: #2494 + // Previous ones are elected. chill is not effective in active era (as era hasn't changed) assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); - // Though the validator preferences has been removed. - assert!(Staking::validators(11) != prefs); + // The chill is still pending. + assert!(!::Validators::contains_key(11)); + // No new era is created. + assert_eq!(current_era, CurrentEra::::get()); }); } @@ -452,8 +463,7 @@ fn nominating_and_rewards_should_work() { ExtBuilder::default() .nominate(false) .validator_pool(true) - .build() - .execute_with(|| { + .build_and_execute(|| { // initial validators -- everyone is actually even. assert_eq_uvec!(validator_controllers(), vec![40, 30]); @@ -479,8 +489,8 @@ fn nominating_and_rewards_should_work() { // the total reward for era 0 let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - >::reward_by_ids(vec![(41, 1)]); - >::reward_by_ids(vec![(31, 1)]); + >::reward_by_ids(vec![(41, 1)]); + >::reward_by_ids(vec![(31, 1)]); mock::start_active_era(1); @@ -521,8 +531,8 @@ fn nominating_and_rewards_should_work() { // the total reward for era 1 let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - >::reward_by_ids(vec![(21, 2)]); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(21, 2)]); + >::reward_by_ids(vec![(11, 1)]); mock::start_active_era(2); @@ -776,7 +786,7 @@ fn forcing_new_era_works() { assert_eq!(active_era(), 1); // no era change. - ForceEra::put(Forcing::ForceNone); + ForceEra::::put(Forcing::ForceNone); start_session(4); assert_eq!(active_era(), 1); @@ -792,7 +802,7 @@ fn forcing_new_era_works() { // back to normal. // this immediately starts a new session. - ForceEra::put(Forcing::NotForcing); + ForceEra::::put(Forcing::NotForcing); start_session(8); assert_eq!(active_era(), 1); @@ -800,7 +810,7 @@ fn forcing_new_era_works() { start_session(9); assert_eq!(active_era(), 2); // forceful change - ForceEra::put(Forcing::ForceAlways); + ForceEra::::put(Forcing::ForceAlways); start_session(10); assert_eq!(active_era(), 2); @@ -812,10 +822,10 @@ fn forcing_new_era_works() { assert_eq!(active_era(), 4); // just one forceful change - ForceEra::put(Forcing::ForceNew); + ForceEra::::put(Forcing::ForceNew); start_session(13); assert_eq!(active_era(), 5); - assert_eq!(ForceEra::get(), Forcing::NotForcing); + assert_eq!(ForceEra::::get(), Forcing::NotForcing); start_session(14); assert_eq!(active_era(), 6); @@ -914,7 +924,7 @@ fn reward_destination_works() { // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); mock::start_active_era(1); mock::make_all_reward_payment(0); @@ -937,7 +947,7 @@ fn reward_destination_works() { // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); mock::start_active_era(2); mock::make_all_reward_payment(1); @@ -965,7 +975,7 @@ fn reward_destination_works() { // Compute total payout now for whole duration as other parameter won't change let total_payout_2 = current_total_payout_for_duration(reward_time_per_era()); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); mock::start_active_era(3); mock::make_all_reward_payment(2); @@ -1012,7 +1022,7 @@ fn validator_payment_prefs_work() { // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); let exposure_1 = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); mock::start_active_era(2); mock::make_all_reward_payment(1); @@ -1238,8 +1248,7 @@ fn rebond_works() { // * it can re-bond a portion of the funds scheduled to unlock. ExtBuilder::default() .nominate(false) - .build() - .execute_with(|| { + .build_and_execute(|| { // Set payee to controller. avoids confusion assert_ok!(Staking::set_payee( Origin::signed(10), @@ -1383,8 +1392,7 @@ fn rebond_is_fifo() { // Rebond should proceed by reversing the most recent bond operations. ExtBuilder::default() .nominate(false) - .build() - .execute_with(|| { + .build_and_execute(|| { // Set payee to controller. avoids confusion assert_ok!(Staking::set_payee( Origin::signed(10), @@ -1505,8 +1513,8 @@ fn reward_to_stake_works() { // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); - >::reward_by_ids(vec![(11, 1)]); - >::reward_by_ids(vec![(21, 1)]); + >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(21, 1)]); // New era --> rewards are paid --> stakes are changed mock::start_active_era(1); @@ -1531,109 +1539,117 @@ fn reward_to_stake_works() { fn on_free_balance_zero_stash_removes_validator() { // Tests that validator storage items are cleaned up when stash is empty // Tests that storage items are untouched when controller is empty - ExtBuilder::default().existential_deposit(10).build_and_execute(|| { - // Check the balance of the validator account - assert_eq!(Balances::free_balance(10), 256); - // Check the balance of the stash account - assert_eq!(Balances::free_balance(11), 256000); - // Check these two accounts are bonded - assert_eq!(Staking::bonded(&11), Some(10)); - - // Set some storage items which we expect to be cleaned up - // Set payee information - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); - - // Check storage items that should be cleaned up - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of controller to 0 - let _ = Balances::slash(&10, Balance::max_value()); - - // Check the balance of the stash account has not been touched - assert_eq!(Balances::free_balance(11), 256000); - // Check these two accounts are still bonded - assert_eq!(Staking::bonded(&11), Some(10)); - - // Check storage items have not changed - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of stash to 0 - let _ = Balances::slash(&11, Balance::max_value()); - // Check total balance of stash - assert_eq!(Balances::total_balance(&11), 10); - - // Reap the stash - assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); - - // Check storage items do not exist - assert!(!>::contains_key(&10)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - }); + ExtBuilder::default() + .existential_deposit(10) + .min_nominator_bond(10) + .min_validator_bond(10) + .build_and_execute(|| { + // Check the balance of the validator account + assert_eq!(Balances::free_balance(10), 256); + // Check the balance of the stash account + assert_eq!(Balances::free_balance(11), 256000); + // Check these two accounts are bonded + assert_eq!(Staking::bonded(&11), Some(10)); + + // Set some storage items which we expect to be cleaned up + // Set payee information + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); + + // Check storage items that should be cleaned up + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of controller to 0 + let _ = Balances::slash(&10, Balance::max_value()); + + // Check the balance of the stash account has not been touched + assert_eq!(Balances::free_balance(11), 256000); + // Check these two accounts are still bonded + assert_eq!(Staking::bonded(&11), Some(10)); + + // Check storage items have not changed + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of stash to 0 + let _ = Balances::slash(&11, Balance::max_value()); + // Check total balance of stash + assert_eq!(Balances::total_balance(&11), 10); + + // Reap the stash + assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); + + // Check storage items do not exist + assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + }); } #[test] fn on_free_balance_zero_stash_removes_nominator() { // Tests that nominator storage items are cleaned up when stash is empty // Tests that storage items are untouched when controller is empty - ExtBuilder::default().existential_deposit(10).build_and_execute(|| { - // Make 10 a nominator - assert_ok!(Staking::nominate(Origin::signed(10), vec![20])); - // Check that account 10 is a nominator - assert!(>::contains_key(11)); - // Check the balance of the nominator account - assert_eq!(Balances::free_balance(10), 256); - // Check the balance of the stash account - assert_eq!(Balances::free_balance(11), 256000); - - // Set payee information - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); - - // Check storage items that should be cleaned up - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of controller to 0 - let _ = Balances::slash(&10, Balance::max_value()); - // Check total balance of account 10 - assert_eq!(Balances::total_balance(&10), 0); - - // Check the balance of the stash account has not been touched - assert_eq!(Balances::free_balance(11), 256000); - // Check these two accounts are still bonded - assert_eq!(Staking::bonded(&11), Some(10)); - - // Check storage items have not changed - assert!(>::contains_key(&10)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - assert!(>::contains_key(&11)); - - // Reduce free_balance of stash to 0 - let _ = Balances::slash(&11, Balance::max_value()); - // Check total balance of stash - assert_eq!(Balances::total_balance(&11), 10); - - // Reap the stash - assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); - - // Check storage items do not exist - assert!(!>::contains_key(&10)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - assert!(!>::contains_key(&11)); - }); + ExtBuilder::default() + .existential_deposit(10) + .min_nominator_bond(10) + .min_validator_bond(10) + .build_and_execute(|| { + // Make 10 a nominator + assert_ok!(Staking::nominate(Origin::signed(10), vec![20])); + // Check that account 10 is a nominator + assert!(>::contains_key(11)); + // Check the balance of the nominator account + assert_eq!(Balances::free_balance(10), 256); + // Check the balance of the stash account + assert_eq!(Balances::free_balance(11), 256000); + + // Set payee information + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); + + // Check storage items that should be cleaned up + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of controller to 0 + let _ = Balances::slash(&10, Balance::max_value()); + // Check total balance of account 10 + assert_eq!(Balances::total_balance(&10), 0); + + // Check the balance of the stash account has not been touched + assert_eq!(Balances::free_balance(11), 256000); + // Check these two accounts are still bonded + assert_eq!(Staking::bonded(&11), Some(10)); + + // Check storage items have not changed + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + + // Reduce free_balance of stash to 0 + let _ = Balances::slash(&11, Balance::max_value()); + // Check total balance of stash + assert_eq!(Balances::total_balance(&11), 10); + + // Reap the stash + assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); + + // Check storage items do not exist + assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + }); } @@ -1709,14 +1725,15 @@ fn bond_with_no_staked_value() { ExtBuilder::default() .validator_count(3) .existential_deposit(5) + .min_nominator_bond(5) + .min_validator_bond(5) .nominate(false) .minimum_validator_count(1) - .build() - .execute_with(|| { + .build_and_execute(|| { // Can't bond with 1 assert_noop!( Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller), - Error::::InsufficientValue, + Error::::InsufficientBond, ); // bonded with absolute minimum value possible. assert_ok!(Staking::bond(Origin::signed(1), 2, 5, RewardDestination::Controller)); @@ -1758,8 +1775,7 @@ fn bond_with_little_staked_value_bounded() { .validator_count(3) .nominate(false) .minimum_validator_count(1) - .build() - .execute_with(|| { + .build_and_execute(|| { // setup assert_ok!(Staking::chill(Origin::signed(30))); assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); @@ -1812,8 +1828,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider() { .validator_count(2) .nominate(false) .minimum_validator_count(1) - .build() - .execute_with(|| { + .build_and_execute(|| { // disable the nominator assert_ok!(Staking::chill(Origin::signed(100))); // make stakes equal. @@ -1860,8 +1875,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider_elected() { .validator_count(2) .nominate(false) .minimum_validator_count(1) - .build() - .execute_with(|| { + .build_and_execute(|| { // disable the nominator assert_ok!(Staking::chill(Origin::signed(100))); // 31/30 will have less stake @@ -1907,8 +1921,7 @@ fn new_era_elects_correct_number_of_validators() { .validator_pool(true) .fair(true) .validator_count(1) - .build() - .execute_with(|| { + .build_and_execute(|| { assert_eq!(Staking::validator_count(), 1); assert_eq!(validator_controllers().len(), 1); @@ -2006,10 +2019,10 @@ fn reward_from_authorship_event_handler_works() { assert_eq!(>::author(), 11); - >::note_author(11); - >::note_uncle(21, 1); + >::note_author(11); + >::note_uncle(21, 1); // Rewarding the same two times works. - >::note_uncle(11, 1); + >::note_uncle(11, 1); // Not mandatory but must be coherent with rewards assert_eq_uvec!(Session::validators(), vec![11, 21]); @@ -2032,13 +2045,13 @@ fn add_reward_points_fns_works() { // Not mandatory but must be coherent with rewards assert_eq_uvec!(Session::validators(), vec![21, 11]); - >::reward_by_ids(vec![ + >::reward_by_ids(vec![ (21, 1), (11, 1), (11, 1), ]); - >::reward_by_ids(vec![ + >::reward_by_ids(vec![ (21, 1), (11, 1), (11, 1), @@ -2081,7 +2094,7 @@ fn era_is_always_same_length() { assert_eq!(Staking::eras_start_session_index(current_era()).unwrap(), session_per_era * 2u32); let session = Session::current_index(); - ForceEra::put(Forcing::ForceNew); + ForceEra::::put(Forcing::ForceNew); advance_session(); advance_session(); assert_eq!(current_era(), 3); @@ -2450,7 +2463,11 @@ fn only_slash_for_max_in_era() { #[test] fn garbage_collection_after_slashing() { // ensures that `SlashingSpans` and `SpanSlash` of an account is removed after reaping. - ExtBuilder::default().existential_deposit(2).build_and_execute(|| { + ExtBuilder::default() + .existential_deposit(2) + .min_nominator_bond(2) + .min_validator_bond(2) + .build_and_execute(|| { assert_eq!(Balances::free_balance(11), 256_000); on_offence_now( @@ -2976,6 +2993,9 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { // * an invalid era to claim doesn't update last_reward // * double claim of one era fails ExtBuilder::default().nominate(true).build_and_execute(|| { + // Consumed weight for all payout_stakers dispatches that fail + let err_weight = weights::SubstrateWeight::::payout_stakers_alive_staked(0); + let init_balance_10 = Balances::total_balance(&10); let init_balance_100 = Balances::total_balance(&100); @@ -2986,13 +3006,13 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { Payee::::insert(11, RewardDestination::Controller); Payee::::insert(101, RewardDestination::Controller); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(reward_time_per_era()); mock::start_active_era(1); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); // Change total issuance in order to modify total payout let _ = Balances::deposit_creating(&999, 1_000_000_000); // Compute total payout now for whole duration as other parameter won't change @@ -3001,7 +3021,7 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { mock::start_active_era(2); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); // Change total issuance in order to modify total payout let _ = Balances::deposit_creating(&999, 1_000_000_000); // Compute total payout now for whole duration as other parameter won't change @@ -3021,19 +3041,19 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { assert_noop!( Staking::payout_stakers(Origin::signed(1337), 11, 0), // Fail: Era out of history - Error::::InvalidEraToReward + Error::::InvalidEraToReward.with_weight(err_weight) ); assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 1)); assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 2)); assert_noop!( Staking::payout_stakers(Origin::signed(1337), 11, 2), // Fail: Double claim - Error::::AlreadyClaimed + Error::::AlreadyClaimed.with_weight(err_weight) ); assert_noop!( Staking::payout_stakers(Origin::signed(1337), 11, active_era), // Fail: Era not finished yet - Error::::InvalidEraToReward + Error::::InvalidEraToReward.with_weight(err_weight) ); // Era 0 can't be rewarded anymore and current era can't be rewarded yet @@ -3162,7 +3182,7 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward( } mock::start_active_era(1); - >::reward_by_ids(vec![(11, 1)]); + >::reward_by_ids(vec![(11, 1)]); // compute and ensure the reward amount is greater than zero. let _ = current_total_payout_for_duration(reward_time_per_era()); @@ -3287,6 +3307,9 @@ fn test_payout_stakers() { fn payout_stakers_handles_basic_errors() { // Here we will test payouts handle all errors. ExtBuilder::default().has_stakers(false).build_and_execute(|| { + // Consumed weight for all payout_stakers dispatches that fail + let err_weight = weights::SubstrateWeight::::payout_stakers_alive_staked(0); + // Same setup as the test above let balance = 1000; bond_validator(11, 10, balance); // Default(64) @@ -3305,9 +3328,15 @@ fn payout_stakers_handles_basic_errors() { mock::start_active_era(2); // Wrong Era, too big - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 2), Error::::InvalidEraToReward); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 2), + Error::::InvalidEraToReward.with_weight(err_weight) + ); // Wrong Staker - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 10, 1), Error::::NotStash); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 10, 1), + Error::::NotStash.with_weight(err_weight) + ); for i in 3..100 { Staking::reward_by_ids(vec![(11, 1)]); @@ -3317,14 +3346,134 @@ fn payout_stakers_handles_basic_errors() { } // We are at era 99, with history depth of 84 // We should be able to payout era 15 through 98 (84 total eras), but not 14 or 99. - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 14), Error::::InvalidEraToReward); - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 99), Error::::InvalidEraToReward); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 14), + Error::::InvalidEraToReward.with_weight(err_weight) + ); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 99), + Error::::InvalidEraToReward.with_weight(err_weight) + ); assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 15)); assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98)); // Can't claim again - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 15), Error::::AlreadyClaimed); - assert_noop!(Staking::payout_stakers(Origin::signed(1337), 11, 98), Error::::AlreadyClaimed); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 15), + Error::::AlreadyClaimed.with_weight(err_weight) + ); + assert_noop!( + Staking::payout_stakers(Origin::signed(1337), 11, 98), + Error::::AlreadyClaimed.with_weight(err_weight) + ); + }); +} + +#[test] +fn payout_stakers_handles_weight_refund() { + // Note: this test relies on the assumption that `payout_stakers_alive_staked` is solely used by + // `payout_stakers` to calculate the weight of each payout op. + ExtBuilder::default().has_stakers(false).build_and_execute(|| { + let max_nom_rewarded = ::MaxNominatorRewardedPerValidator::get(); + // Make sure the configured value is meaningful for our use. + assert!(max_nom_rewarded >= 4); + let half_max_nom_rewarded = max_nom_rewarded / 2; + // Sanity check our max and half max nominator quantities. + assert!(half_max_nom_rewarded > 0); + assert!(max_nom_rewarded > half_max_nom_rewarded); + + let max_nom_rewarded_weight + = ::WeightInfo::payout_stakers_alive_staked(max_nom_rewarded); + let half_max_nom_rewarded_weight + = ::WeightInfo::payout_stakers_alive_staked(half_max_nom_rewarded); + let zero_nom_payouts_weight = ::WeightInfo::payout_stakers_alive_staked(0); + assert!(zero_nom_payouts_weight > 0); + assert!(half_max_nom_rewarded_weight > zero_nom_payouts_weight); + assert!(max_nom_rewarded_weight > half_max_nom_rewarded_weight); + + let balance = 1000; + bond_validator(11, 10, balance); + + /* Era 1 */ + start_active_era(1); + + // Reward just the validator. + Staking::reward_by_ids(vec![(11, 1)]); + + // Add some `half_max_nom_rewarded` nominators who will start backing the validator in the + // next era. + for i in 0..half_max_nom_rewarded { + bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]); + } + + /* Era 2 */ + start_active_era(2); + + // Collect payouts when there are no nominators + let call = TestRuntimeCall::Staking(StakingCall::payout_stakers(11, 1)); + let info = call.get_dispatch_info(); + let result = call.dispatch(Origin::signed(20)); + assert_ok!(result); + assert_eq!( + extract_actual_weight(&result, &info), + zero_nom_payouts_weight + ); + + // The validator is not rewarded in this era; so there will be zero payouts to claim for this era. + + /* Era 3 */ + start_active_era(3); + + // Collect payouts for an era where the validator did not receive any points. + let call = TestRuntimeCall::Staking(StakingCall::payout_stakers(11, 2)); + let info = call.get_dispatch_info(); + let result = call.dispatch(Origin::signed(20)); + assert_ok!(result); + assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight); + + // Reward the validator and its nominators. + Staking::reward_by_ids(vec![(11, 1)]); + + /* Era 4 */ + start_active_era(4); + + // Collect payouts when the validator has `half_max_nom_rewarded` nominators. + let call = TestRuntimeCall::Staking(StakingCall::payout_stakers(11, 3)); + let info = call.get_dispatch_info(); + let result = call.dispatch(Origin::signed(20)); + assert_ok!(result); + assert_eq!(extract_actual_weight(&result, &info), half_max_nom_rewarded_weight); + + // Add enough nominators so that we are at the limit. They will be active nominators + // in the next era. + for i in half_max_nom_rewarded..max_nom_rewarded { + bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]); + } + + /* Era 5 */ + start_active_era(5); + // We now have `max_nom_rewarded` nominators actively nominating our validator. + + // Reward the validator so we can collect for everyone in the next era. + Staking::reward_by_ids(vec![(11, 1)]); + + /* Era 6 */ + start_active_era(6); + + // Collect payouts when the validator had `half_max_nom_rewarded` nominators. + let call = TestRuntimeCall::Staking(StakingCall::payout_stakers(11, 5)); + let info = call.get_dispatch_info(); + let result = call.dispatch(Origin::signed(20)); + assert_ok!(result); + assert_eq!(extract_actual_weight(&result, &info), max_nom_rewarded_weight); + + // Try and collect payouts for an era that has already been collected. + let call = TestRuntimeCall::Staking(StakingCall::payout_stakers(11, 5)); + let info = call.get_dispatch_info(); + let result = call.dispatch(Origin::signed(20)); + assert!(result.is_err()); + // When there is an error the consumed weight == weight when there are 0 nominator payouts. + assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight); }); } @@ -3375,7 +3524,7 @@ fn offences_weight_calculated_correctly() { ExtBuilder::default().nominate(true).build_and_execute(|| { // On offence with zero offenders: 4 Reads, 1 Write let zero_offence_weight = ::DbWeight::get().reads_writes(4, 1); - assert_eq!(Staking::on_offence(&[], &[Perbill::from_percent(50)], 0), Ok(zero_offence_weight)); + assert_eq!(Staking::on_offence(&[], &[Perbill::from_percent(50)], 0), zero_offence_weight); // On Offence with N offenders, Unapplied: 4 Reads, 1 Write + 4 Reads, 5 Writes let n_offence_unapplied_weight = ::DbWeight::get().reads_writes(4, 1) @@ -3388,7 +3537,7 @@ fn offences_weight_calculated_correctly() { reporters: vec![], } ).collect(); - assert_eq!(Staking::on_offence(&offenders, &[Perbill::from_percent(50)], 0), Ok(n_offence_unapplied_weight)); + assert_eq!(Staking::on_offence(&offenders, &[Perbill::from_percent(50)], 0), n_offence_unapplied_weight); // On Offence with one offenders, Applied let one_offender = [ @@ -3409,7 +3558,7 @@ fn offences_weight_calculated_correctly() { // `reward_cost` * reporters (1) + ::DbWeight::get().reads_writes(2, 2); - assert_eq!(Staking::on_offence(&one_offender, &[Perbill::from_percent(50)], 0), Ok(one_offence_unapplied_weight)); + assert_eq!(Staking::on_offence(&one_offender, &[Perbill::from_percent(50)], 0), one_offence_unapplied_weight); }); } @@ -3575,6 +3724,8 @@ fn session_buffering_no_offset() { fn cannot_rebond_to_lower_than_ed() { ExtBuilder::default() .existential_deposit(10) + .min_nominator_bond(10) + .min_validator_bond(10) .build_and_execute(|| { // stash must have more balance than bonded for this to work. assert_eq!(Balances::free_balance(&21), 512_000); @@ -3591,7 +3742,8 @@ fn cannot_rebond_to_lower_than_ed() { } ); - // unbond all of it. + // unbond all of it. must be chilled first. + assert_ok!(Staking::chill(Origin::signed(20))); assert_ok!(Staking::unbond(Origin::signed(20), 1000)); assert_eq!( Staking::ledger(&20).unwrap(), @@ -3607,7 +3759,7 @@ fn cannot_rebond_to_lower_than_ed() { // now bond a wee bit more assert_noop!( Staking::rebond(Origin::signed(20), 5), - Error::::InsufficientValue, + Error::::InsufficientBond, ); }) } @@ -3616,6 +3768,8 @@ fn cannot_rebond_to_lower_than_ed() { fn cannot_bond_extra_to_lower_than_ed() { ExtBuilder::default() .existential_deposit(10) + .min_nominator_bond(10) + .min_validator_bond(10) .build_and_execute(|| { // stash must have more balance than bonded for this to work. assert_eq!(Balances::free_balance(&21), 512_000); @@ -3632,7 +3786,8 @@ fn cannot_bond_extra_to_lower_than_ed() { } ); - // unbond all of it. + // unbond all of it. must be chilled first. + assert_ok!(Staking::chill(Origin::signed(20))); assert_ok!(Staking::unbond(Origin::signed(20), 1000)); assert_eq!( Staking::ledger(&20).unwrap(), @@ -3651,7 +3806,7 @@ fn cannot_bond_extra_to_lower_than_ed() { // now bond a wee bit more assert_noop!( Staking::bond_extra(Origin::signed(21), 5), - Error::::InsufficientValue, + Error::::InsufficientBond, ); }) } @@ -3661,6 +3816,8 @@ fn do_not_die_when_active_is_ed() { let ed = 10; ExtBuilder::default() .existential_deposit(ed) + .min_nominator_bond(ed) + .min_validator_bond(ed) .build_and_execute(|| { // initial stuff. assert_eq!( @@ -3693,6 +3850,14 @@ fn do_not_die_when_active_is_ed() { }) } +#[test] +fn on_finalize_weight_is_nonzero() { + ExtBuilder::default().build_and_execute(|| { + let on_finalize_weight = ::DbWeight::get().reads(1); + assert!(>::on_initialize(1) >= on_finalize_weight); + }) +} + mod election_data_provider { use super::*; use frame_election_provider_support::ElectionDataProvider; @@ -3732,7 +3897,7 @@ mod election_data_provider { #[test] fn voters_include_self_vote() { - ExtBuilder::default().nominate(false).build().execute_with(|| { + ExtBuilder::default().nominate(false).build_and_execute(|| { assert!(>::iter().map(|(x, _)| x).all(|v| Staking::voters(None) .unwrap() .0 @@ -3744,7 +3909,7 @@ mod election_data_provider { #[test] fn voters_exclude_slashed() { - ExtBuilder::default().build().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); assert_eq!( >::voters(None) @@ -3790,7 +3955,7 @@ mod election_data_provider { #[test] fn respects_len_limits() { - ExtBuilder::default().build().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_eq!(Staking::voters(Some(1)).unwrap_err(), "Voter snapshot too big"); assert_eq!(Staking::targets(Some(1)).unwrap_err(), "Target snapshot too big"); }); @@ -3798,7 +3963,7 @@ mod election_data_provider { #[test] fn estimate_next_election_works() { - ExtBuilder::default().session_per_era(5).period(5).build().execute_with(|| { + ExtBuilder::default().session_per_era(5).period(5).build_and_execute(|| { // first session is always length 0. for b in 1..20 { run_to_block(b); @@ -3811,7 +3976,7 @@ mod election_data_provider { assert_eq!(staking_events().len(), 1); assert_eq!( *staking_events().last().unwrap(), - RawEvent::StakingElection + Event::StakingElection ); for b in 21..45 { @@ -3825,8 +3990,161 @@ mod election_data_provider { assert_eq!(staking_events().len(), 3); assert_eq!( *staking_events().last().unwrap(), - RawEvent::StakingElection + Event::StakingElection + ); + + Staking::force_no_eras(Origin::root()).unwrap(); + assert_eq!(Staking::next_election_prediction(System::block_number()), u64::max_value()); + + Staking::force_new_era_always(Origin::root()).unwrap(); + assert_eq!(Staking::next_election_prediction(System::block_number()), 45 + 5); + + Staking::force_new_era(Origin::root()).unwrap(); + assert_eq!(Staking::next_election_prediction(System::block_number()), 45 + 5); + + // Do a fail election + MinimumValidatorCount::::put(1000); + run_to_block(50); + // Election: failed, next session is a new election + assert_eq!(Staking::next_election_prediction(System::block_number()), 50 + 5); + // The new era is still forced until a new era is planned. + assert_eq!(ForceEra::::get(), Forcing::ForceNew); + + MinimumValidatorCount::::put(2); + run_to_block(55); + assert_eq!(Staking::next_election_prediction(System::block_number()), 55 + 25); + assert_eq!(staking_events().len(), 6); + assert_eq!( + *staking_events().last().unwrap(), + Event::StakingElection ); + // The new era has been planned, forcing is changed from `ForceNew` to `NotForcing`. + assert_eq!(ForceEra::::get(), Forcing::NotForcing); + }) + } + + #[test] + #[should_panic] + fn count_check_works() { + ExtBuilder::default().build_and_execute(|| { + // We should never insert into the validators or nominators map directly as this will + // not keep track of the count. This test should panic as we verify the count is accurate + // after every test using the `post_checks` in `mock`. + Validators::::insert(987654321, ValidatorPrefs::default()); + Nominators::::insert(987654321, Nominations { + targets: vec![], + submitted_in: Default::default(), + suppressed: false, + }); + }) + } + + #[test] + fn min_bond_checks_work() { + ExtBuilder::default() + .existential_deposit(100) + .min_nominator_bond(1_000) + .min_validator_bond(1_500) + .build_and_execute(|| { + // 500 is not enough for any role + assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller)); + assert_noop!(Staking::nominate(Origin::signed(4), vec![1]), Error::::InsufficientBond); + assert_noop!(Staking::validate(Origin::signed(4), ValidatorPrefs::default()), Error::::InsufficientBond); + + // 1000 is enough for nominator + assert_ok!(Staking::bond_extra(Origin::signed(3), 500)); + assert_ok!(Staking::nominate(Origin::signed(4), vec![1])); + assert_noop!(Staking::validate(Origin::signed(4), ValidatorPrefs::default()), Error::::InsufficientBond); + + // 1500 is enough for validator + assert_ok!(Staking::bond_extra(Origin::signed(3), 500)); + assert_ok!(Staking::nominate(Origin::signed(4), vec![1])); + assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); + + // Can't unbond anything as validator + assert_noop!(Staking::unbond(Origin::signed(4), 500), Error::::InsufficientBond); + + // Once they are a nominator, they can unbond 500 + assert_ok!(Staking::nominate(Origin::signed(4), vec![1])); + assert_ok!(Staking::unbond(Origin::signed(4), 500)); + assert_noop!(Staking::unbond(Origin::signed(4), 500), Error::::InsufficientBond); + + // Once they are chilled they can unbond everything + assert_ok!(Staking::chill(Origin::signed(4))); + assert_ok!(Staking::unbond(Origin::signed(4), 1000)); + }) + } + + #[test] + fn chill_other_works() { + ExtBuilder::default() + .existential_deposit(100) + .min_nominator_bond(1_000) + .min_validator_bond(1_500) + .build_and_execute(|| { + // Nominator + assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller)); + assert_ok!(Staking::nominate(Origin::signed(2), vec![1])); + + // Validator + assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller)); + assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); + + // Can't chill these users + assert_noop!(Staking::chill_other(Origin::signed(1), 2), Error::::CannotChillOther); + assert_noop!(Staking::chill_other(Origin::signed(1), 4), Error::::CannotChillOther); + + // Change the minimum bond + assert_ok!(Staking::update_staking_limits(Origin::root(), 1_500, 2_000, None, None)); + + // Users can now be chilled + assert_ok!(Staking::chill_other(Origin::signed(1), 2)); + assert_ok!(Staking::chill_other(Origin::signed(1), 4)); + }) + } + + #[test] + fn capped_stakers_works() { + ExtBuilder::default().build_and_execute(|| { + let validator_count = CounterForValidators::::get(); + assert_eq!(validator_count, 3); + let nominator_count = CounterForNominators::::get(); + assert_eq!(nominator_count, 1); + + // Change the maximums + let max = 10; + assert_ok!(Staking::update_staking_limits(Origin::root(), 10, 10, Some(max), Some(max))); + + // can create `max - validator_count` validators + assert_ok!(testing_utils::create_validators::(max - validator_count, 100)); + + // but no more + let (_, last_validator) = testing_utils::create_stash_controller::( + 1337, 100, RewardDestination::Controller, + ).unwrap(); + assert_noop!( + Staking::validate(Origin::signed(last_validator), ValidatorPrefs::default()), + Error::::TooManyValidators, + ); + + // same with nominators + for i in 0 .. max - nominator_count { + let (_, controller) = testing_utils::create_stash_controller::( + i + 10_000_000, 100, RewardDestination::Controller, + ).unwrap(); + assert_ok!(Staking::nominate(Origin::signed(controller), vec![1])); + } + + // one more is too many + let (_, last_nominator) = testing_utils::create_stash_controller::( + 20_000_000, 100, RewardDestination::Controller, + ).unwrap(); + assert_noop!(Staking::nominate(Origin::signed(last_nominator), vec![1]), Error::::TooManyNominators); + + // No problem when we set to `None` again + assert_ok!(Staking::update_staking_limits(Origin::root(), 10, 10, None, None)); + assert_ok!(Staking::nominate(Origin::signed(last_nominator), vec![1])); + assert_ok!(Staking::validate(Origin::signed(last_validator), ValidatorPrefs::default())); }) } } diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 520bef8c539b2..980b0855fbd81 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-03-19, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-06-15, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -70,175 +70,176 @@ pub trait WeightInfo { fn new_era(v: u32, n: u32, ) -> Weight; fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight; fn get_npos_targets(v: u32, ) -> Weight; + fn update_staking_limits() -> Weight; + fn chill_other() -> Weight; } /// Weights for pallet_staking using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn bond() -> Weight { - (82_121_000 as Weight) + (91_278_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (61_899_000 as Weight) + (69_833_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (56_392_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) + (75_020_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (57_382_000 as Weight) - // Standard Error: 0 - .saturating_add((70_000 as Weight).saturating_mul(s as Weight)) + (63_898_000 as Weight) + // Standard Error: 1_000 + .saturating_add((50_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (92_185_000 as Weight) + (103_717_000 as Weight) // Standard Error: 1_000 - .saturating_add((2_844_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add((2_942_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (16_892_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) + (40_702_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (27_411_000 as Weight) - // Standard Error: 14_000 - .saturating_add((19_272_000 as Weight).saturating_mul(k as Weight)) + (33_572_000 as Weight) + // Standard Error: 18_000 + .saturating_add((20_771_000 as Weight).saturating_mul(k as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (30_188_000 as Weight) - // Standard Error: 24_000 - .saturating_add((5_666_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) + (53_561_000 as Weight) + // Standard Error: 34_000 + .saturating_add((6_652_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (15_870_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + (21_489_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) } fn set_payee() -> Weight { - (13_853_000 as Weight) + (14_514_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (30_291_000 as Weight) + (32_598_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_397_000 as Weight) + (2_477_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_627_000 as Weight) + (2_743_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_679_000 as Weight) + (2_784_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_643_000 as Weight) + (2_749_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_871_000 as Weight) + (2_798_000 as Weight) // Standard Error: 0 - .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (65_876_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_832_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + (70_372_000 as Weight) + // Standard Error: 13_000 + .saturating_add((3_029_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_896_640_000 as Weight) - // Standard Error: 391_000 - .saturating_add((34_808_000 as Weight).saturating_mul(s as Weight)) + (3_436_822_000 as Weight) + // Standard Error: 221_000 + .saturating_add((19_799_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (137_975_000 as Weight) - // Standard Error: 20_000 - .saturating_add((54_061_000 as Weight).saturating_mul(n as Weight)) + (132_018_000 as Weight) + // Standard Error: 27_000 + .saturating_add((61_340_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (163_885_000 as Weight) - // Standard Error: 20_000 - .saturating_add((68_096_000 as Weight).saturating_mul(n as Weight)) + (158_346_000 as Weight) + // Standard Error: 61_000 + .saturating_add((77_147_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(11 as Weight)) .saturating_add(T::DbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (37_847_000 as Weight) - // Standard Error: 1_000 - .saturating_add((89_000 as Weight).saturating_mul(l as Weight)) + (57_756_000 as Weight) + // Standard Error: 2_000 + .saturating_add((79_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 69_000 - .saturating_add((34_413_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 100_000 + .saturating_add((44_873_000 as Weight).saturating_mul(e as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) .saturating_add(T::DbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (69_257_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_819_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + (75_073_000 as Weight) + // Standard Error: 4_000 + .saturating_add((2_988_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 1_013_000 - .saturating_add((382_529_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 50_000 - .saturating_add((63_170_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 1_146_000 + .saturating_add((362_986_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 57_000 + .saturating_add((60_216_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(v as Weight))) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().writes(9 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(v as Weight))) } fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight { (0 as Weight) - // Standard Error: 90_000 - .saturating_add((27_108_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 90_000 - .saturating_add((29_962_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 1_228_000 - .saturating_add((26_080_000 as Weight).saturating_mul(s as Weight)) + // Standard Error: 230_000 + .saturating_add((35_891_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 230_000 + .saturating_add((37_854_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 7_842_000 + .saturating_add((32_492_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(v as Weight))) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) @@ -246,179 +247,187 @@ impl WeightInfo for SubstrateWeight { } fn get_npos_targets(v: u32, ) -> Weight { (0 as Weight) - // Standard Error: 32_000 - .saturating_add((11_220_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 74_000 + .saturating_add((16_370_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(v as Weight))) } + fn update_staking_limits() -> Weight { + (6_398_000 as Weight) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn chill_other() -> Weight { + (44_694_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } } // For backwards compatibility and tests impl WeightInfo for () { fn bond() -> Weight { - (82_121_000 as Weight) + (91_278_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (61_899_000 as Weight) + (69_833_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (56_392_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + (75_020_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (57_382_000 as Weight) - // Standard Error: 0 - .saturating_add((70_000 as Weight).saturating_mul(s as Weight)) + (63_898_000 as Weight) + // Standard Error: 1_000 + .saturating_add((50_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (92_185_000 as Weight) + (103_717_000 as Weight) // Standard Error: 1_000 - .saturating_add((2_844_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + .saturating_add((2_942_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (16_892_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + (40_702_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (27_411_000 as Weight) - // Standard Error: 14_000 - .saturating_add((19_272_000 as Weight).saturating_mul(k as Weight)) + (33_572_000 as Weight) + // Standard Error: 18_000 + .saturating_add((20_771_000 as Weight).saturating_mul(k as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (30_188_000 as Weight) - // Standard Error: 24_000 - .saturating_add((5_666_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + (53_561_000 as Weight) + // Standard Error: 34_000 + .saturating_add((6_652_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (15_870_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + (21_489_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) } fn set_payee() -> Weight { - (13_853_000 as Weight) + (14_514_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (30_291_000 as Weight) + (32_598_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_397_000 as Weight) + (2_477_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_627_000 as Weight) + (2_743_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_679_000 as Weight) + (2_784_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_643_000 as Weight) + (2_749_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_871_000 as Weight) + (2_798_000 as Weight) // Standard Error: 0 - .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((5_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (65_876_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_832_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + (70_372_000 as Weight) + // Standard Error: 13_000 + .saturating_add((3_029_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_896_640_000 as Weight) - // Standard Error: 391_000 - .saturating_add((34_808_000 as Weight).saturating_mul(s as Weight)) + (3_436_822_000 as Weight) + // Standard Error: 221_000 + .saturating_add((19_799_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (137_975_000 as Weight) - // Standard Error: 20_000 - .saturating_add((54_061_000 as Weight).saturating_mul(n as Weight)) + (132_018_000 as Weight) + // Standard Error: 27_000 + .saturating_add((61_340_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (163_885_000 as Weight) - // Standard Error: 20_000 - .saturating_add((68_096_000 as Weight).saturating_mul(n as Weight)) + (158_346_000 as Weight) + // Standard Error: 61_000 + .saturating_add((77_147_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(11 as Weight)) .saturating_add(RocksDbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (37_847_000 as Weight) - // Standard Error: 1_000 - .saturating_add((89_000 as Weight).saturating_mul(l as Weight)) + (57_756_000 as Weight) + // Standard Error: 2_000 + .saturating_add((79_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 69_000 - .saturating_add((34_413_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 100_000 + .saturating_add((44_873_000 as Weight).saturating_mul(e as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) .saturating_add(RocksDbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (69_257_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_819_000 as Weight).saturating_mul(s as Weight)) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + (75_073_000 as Weight) + // Standard Error: 4_000 + .saturating_add((2_988_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 1_013_000 - .saturating_add((382_529_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 50_000 - .saturating_add((63_170_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 1_146_000 + .saturating_add((362_986_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 57_000 + .saturating_add((60_216_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(v as Weight))) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) - .saturating_add(RocksDbWeight::get().writes(9 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(v as Weight))) } fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight { (0 as Weight) - // Standard Error: 90_000 - .saturating_add((27_108_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 90_000 - .saturating_add((29_962_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 1_228_000 - .saturating_add((26_080_000 as Weight).saturating_mul(s as Weight)) + // Standard Error: 230_000 + .saturating_add((35_891_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 230_000 + .saturating_add((37_854_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 7_842_000 + .saturating_add((32_492_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(v as Weight))) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) @@ -426,9 +435,18 @@ impl WeightInfo for () { } fn get_npos_targets(v: u32, ) -> Weight { (0 as Weight) - // Standard Error: 32_000 - .saturating_add((11_220_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 74_000 + .saturating_add((16_370_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(v as Weight))) } + fn update_staking_limits() -> Weight { + (6_398_000 as Weight) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn chill_other() -> Weight { + (44_694_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 220e7a06bdf31..362c4c5a0a73b 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -87,6 +87,124 @@ pub const LOG_TARGET: &'static str = "runtime::frame-support"; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Never {} +/// Generate a new type alias for [`storage::types::value::StorageValue`], +/// [`storage::types::value::StorageMap`] and [`storage::types::value::StorageDoubleMap`]. +/// +/// Useful for creating a *storage-like* struct for test and migrations. +/// +///``` +/// # use frame_support::generate_storage_alias; +/// use frame_support::codec; +/// use frame_support::Twox64Concat; +/// // generate a storage value with type u32. +/// generate_storage_alias!(Prefix, StorageName => Value); +/// +/// // generate a double map from `(u32, u32)` (with hasher `Twox64Concat`) to `Vec` +/// generate_storage_alias!( +/// OtherPrefix, OtherStorageName => DoubleMap< +/// (u32, u32), +/// (u32, u32), +/// Vec +/// > +/// ); +/// +/// // generate a map from `Config::AccountId` (with hasher `Twox64Concat`) to `Vec` +/// trait Config { type AccountId: codec::FullCodec; } +/// generate_storage_alias!( +/// Prefix, GenericStorage => Map<(Twox64Concat, T::AccountId), Vec> +/// ); +/// # fn main() {} +///``` +#[macro_export] +macro_rules! generate_storage_alias { + // without generic for $name. + ($pallet:ident, $name:ident => Map<($key:ty, $hasher:ty), $value:ty>) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + type $name = $crate::storage::types::StorageMap< + [<$name Instance>], + $hasher, + $key, + $value, + >; + } + }; + ($pallet:ident, $name:ident => DoubleMap<($key1:ty, $hasher1:ty), ($key2:ty, $hasher2:ty), $value:ty>) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + type $name = $crate::storage::types::StorageMap< + [<$name Instance>], + $hasher1, + $key1, + $hasher2, + $key2, + $value, + >; + } + }; + ($pallet:ident, $name:ident => Value<$value:ty>) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + type $name = $crate::storage::types::StorageValue< + [<$name Instance>], + $value, + >; + } + }; + // with generic for $name. + ($pallet:ident, $name:ident<$t:ident : $bounds:tt> => Map<($key:ty, $hasher:ty), $value:ty>) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + #[allow(type_alias_bounds)] + type $name<$t : $bounds> = $crate::storage::types::StorageMap< + [<$name Instance>], + $key, + $hasher, + $value, + >; + } + }; + ( + $pallet:ident, + $name:ident<$t:ident : $bounds:tt> + => DoubleMap<($key1:ty, $hasher1:ty), ($key2:ty, $hasher2:ty), $value:ty>) + => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + #[allow(type_alias_bounds)] + type $name<$t : $bounds> = $crate::storage::types::StorageMap< + [<$name Instance>], + $key1, + $hasher1, + $key2, + $hasher2, + $value, + >; + } + }; + ($pallet:ident, $name:ident<$t:ident : $bounds:tt> => Value<$value:ty>) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + #[allow(type_alias_bounds)] + type $name<$t : $bounds> = $crate::storage::types::StorageValue< + [<$name Instance>], + $value, + $crate::storage::types::ValueQuery, + >; + } + }; + // helper used in all arms. + (@GENERATE_INSTANCE_STRUCT $pallet:ident, $name:ident) => { + $crate::paste::paste! { + struct [<$name Instance>]; + impl $crate::traits::StorageInstance for [<$name Instance>] { + fn pallet_prefix() -> &'static str { stringify!($pallet) } + const STORAGE_PREFIX: &'static str = stringify!($name); + } + } + } +} + /// Create new implementations of the [`Get`](crate::traits::Get) trait. /// /// The so-called parameter type can be created in four different ways: diff --git a/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr b/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr index c5319da851078..7648f5c1bfb33 100644 --- a/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr +++ b/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr @@ -1,7 +1,6 @@ error: `System` pallet declaration is missing. Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event},` --> $DIR/missing_system_module.rs:8:2 | -8 | { - | _____^ +8 | / { 9 | | } | |_____^ diff --git a/frame/support/test/tests/derive_no_bound_ui/eq.stderr b/frame/support/test/tests/derive_no_bound_ui/eq.stderr index bbd907adecb33..fce13d6f17f06 100644 --- a/frame/support/test/tests/derive_no_bound_ui/eq.stderr +++ b/frame/support/test/tests/derive_no_bound_ui/eq.stderr @@ -4,4 +4,9 @@ error[E0277]: can't compare `Foo` with `Foo` 6 | struct Foo { | ^^^ no implementation for `Foo == Foo` | + ::: $RUST/core/src/cmp.rs + | + | pub trait Eq: PartialEq { + | --------------- required by this bound in `std::cmp::Eq` + | = help: the trait `PartialEq` is not implemented for `Foo` diff --git a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr index 86968221cf30c..e2111f95a122e 100644 --- a/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr +++ b/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr @@ -1,24 +1,34 @@ -error[E0277]: the trait bound `pallet::Call: Decode` is not satisfied +error[E0277]: the trait bound `pallet::Call: pallet::_::_parity_scale_codec::Encode` is not satisfied --> $DIR/call_argument_invalid_bound_2.rs:17:12 | 17 | #[pallet::call] - | ^^^^ the trait `Decode` is not implemented for `pallet::Call` + | ^^^^ the trait `pallet::_::_parity_scale_codec::Encode` is not implemented for `pallet::Call` | ::: $WORKSPACE/frame/support/src/dispatch.rs | | type Call: UnfilteredDispatchable + Codec + Clone + PartialEq + Eq; | ----- required by this bound in `frame_support::Callable::Call` + | +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +17 | #[pallet::call where pallet::Call: pallet::_::_parity_scale_codec::Encode] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `pallet::Call: pallet::_::_parity_scale_codec::Encode` is not satisfied +error[E0277]: the trait bound `pallet::Call: Decode` is not satisfied --> $DIR/call_argument_invalid_bound_2.rs:17:12 | 17 | #[pallet::call] - | ^^^^ the trait `pallet::_::_parity_scale_codec::Encode` is not implemented for `pallet::Call` + | ^^^^ the trait `Decode` is not implemented for `pallet::Call` | ::: $WORKSPACE/frame/support/src/dispatch.rs | | type Call: UnfilteredDispatchable + Codec + Clone + PartialEq + Eq; | ----- required by this bound in `frame_support::Callable::Call` + | +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +17 | #[pallet::call where pallet::Call: Decode] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0369]: binary operation `==` cannot be applied to type `&::Bar` --> $DIR/call_argument_invalid_bound_2.rs:20:37 diff --git a/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr b/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr index 0379448f694fc..61c27750a0a8c 100644 --- a/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr +++ b/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr @@ -1,5 +1,15 @@ -error[E0107]: wrong number of type arguments: expected 1, found 0 - --> $DIR/hooks_invalid_item.rs:12:18 - | -12 | impl Hooks for Pallet {} - | ^^^^^ expected 1 type argument +error[E0107]: missing generics for trait `Hooks` + --> $DIR/hooks_invalid_item.rs:12:18 + | +12 | impl Hooks for Pallet {} + | ^^^^^ expected 1 type argument + | +note: trait defined here, with 1 type parameter: `BlockNumber` + --> $DIR/traits.rs:2095:11 + | +2095 | pub trait Hooks { + | ^^^^^ ----------- +help: use angle brackets to add missing type argument + | +12 | impl Hooks for Pallet {} + | ^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs index 05cedbdb91a07..da753b2d26f3d 100644 --- a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs +++ b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs @@ -46,7 +46,7 @@ frame_support::decl_module! { const Foo: u32 = u32::max_value(); #[weight = 0] - fn accumulate_dummy(origin, increase_by: T::Balance) { + fn accumulate_dummy(_origin, _increase_by: T::Balance) { unimplemented!(); } diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs index 19858731b3a09..c4d7cf01ae215 100644 --- a/frame/support/test/tests/system.rs +++ b/frame/support/test/tests/system.rs @@ -36,7 +36,7 @@ pub trait Config: 'static + Eq + Clone { frame_support::decl_module! { pub struct Module for enum Call where origin: T::Origin, system=self { #[weight = 0] - fn noop(origin) {} + fn noop(_origin) {} } } diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 3a8089519fac5..d23ee1e6c9cea 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -41,14 +41,14 @@ pub mod example { decl_module! { pub struct Module for enum Call where origin: ::Origin { - #[weight = *weight] - fn noop(_origin, weight: Weight) { } + #[weight = *_weight] + fn noop(_origin, _weight: Weight) { } - #[weight = *start_weight] + #[weight = *_start_weight] fn foobar( origin, err: bool, - start_weight: Weight, + _start_weight: Weight, end_weight: Option, ) -> DispatchResultWithPostInfo { let _ = ensure_signed(origin)?; diff --git a/primitives/api/test/tests/decl_and_impl.rs b/primitives/api/test/tests/decl_and_impl.rs index 1f7ccf2712d63..54fb37133f468 100644 --- a/primitives/api/test/tests/decl_and_impl.rs +++ b/primitives/api/test/tests/decl_and_impl.rs @@ -156,19 +156,19 @@ fn test_client_side_function_signature() { #[test] fn check_runtime_api_info() { - assert_eq!(&Api::::ID, &runtime_decl_for_Api::ID); - assert_eq!(Api::::VERSION, runtime_decl_for_Api::VERSION); - assert_eq!(Api::::VERSION, 1); + assert_eq!(&>::ID, &runtime_decl_for_Api::ID); + assert_eq!(>::VERSION, runtime_decl_for_Api::VERSION); + assert_eq!(>::VERSION, 1); assert_eq!( - ApiWithCustomVersion::::VERSION, + >::VERSION, runtime_decl_for_ApiWithCustomVersion::VERSION, ); assert_eq!( - &ApiWithCustomVersion::::ID, + &>::ID, &runtime_decl_for_ApiWithCustomVersion::ID, ); - assert_eq!(ApiWithCustomVersion::::VERSION, 2); + assert_eq!(>::VERSION, 2); } fn check_runtime_api_versions_contains() { diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index ea297077e351c..d0ce921d9d342 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -127,6 +127,34 @@ pub trait Saturating { /// Saturating exponentiation. Compute `self.pow(exp)`, saturating at the numeric bounds /// instead of overflowing. fn saturating_pow(self, exp: usize) -> Self; + + /// Increment self by one, saturating. + fn saturating_inc(&mut self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_add(One::one()); + } + + /// Decrement self by one, saturating at zero. + fn saturating_dec(&mut self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_sub(One::one()); + } + + /// Increment self by some `amount`, saturating. + fn saturating_accrue(&mut self, amount: Self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_add(amount); + } + + /// Decrement self by some `amount`, saturating at zero. + fn saturating_reduce(&mut self, amount: Self) where Self: One { + let mut o = Self::one(); + sp_std::mem::swap(&mut o, self); + *self = o.saturating_sub(amount); + } } impl Saturating for T { diff --git a/primitives/core/benches/bench.rs b/primitives/core/benches/bench.rs index dc57af459daa0..225d562075c3f 100644 --- a/primitives/core/benches/bench.rs +++ b/primitives/core/benches/bench.rs @@ -12,13 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. - - #[macro_use] extern crate criterion; -use criterion::{Criterion, black_box, Bencher, Fun}; -use std::time::Duration; +use criterion::{Criterion, black_box, Bencher, BenchmarkId}; use sp_core::crypto::Pair as _; use sp_core::hashing::{twox_128, blake2_128}; @@ -49,87 +46,133 @@ fn bench_twox_128(b: &mut Bencher, key: &Vec) { } fn bench_hash_128_fix_size(c: &mut Criterion) { + let mut group = c.benchmark_group("fix size hashing"); + let key = get_key(MAX_KEY_SIZE); - let blake_fn = Fun::new("blake2_128", bench_blake2_128); - let twox_fn = Fun::new("twox_128", bench_twox_128); - let fns = vec![blake_fn, twox_fn]; - c.bench_functions("fixed size hashing", fns, key); + group.bench_with_input("blake2_128", &key, bench_blake2_128); + group.bench_with_input("twox_128", &key, bench_twox_128); + + group.finish(); } fn bench_hash_128_dyn_size(c: &mut Criterion) { - let mut keys = Vec::new(); + let mut group = c.benchmark_group("dyn size hashing"); + for i in (2..MAX_KEY_SIZE).step_by(4) { - keys.push(get_key(i).clone()) + let key = get_key(i); + + group.bench_with_input( + BenchmarkId::new("blake2_128", format!("{}", i)), + &key, + bench_blake2_128, + ); + group.bench_with_input( + BenchmarkId::new("twox_128", format!("{}", i)), + &key, + bench_twox_128, + ); } - c.bench_function_over_inputs("dyn size hashing - blake2", |b, key| bench_blake2_128(b, &key), keys.clone()); - c.bench_function_over_inputs("dyn size hashing - twox", |b, key| bench_twox_128(b, &key), keys); + group.finish(); } fn bench_ed25519(c: &mut Criterion) { - c.bench_function_over_inputs("signing - ed25519", |b, &msg_size| { + let mut group = c.benchmark_group("ed25519"); + + for msg_size in vec![32, 1024, 1024 * 1024] { let msg = (0..msg_size) .map(|_| rand::random::()) .collect::>(); let key = sp_core::ed25519::Pair::generate().0; - b.iter(|| key.sign(&msg)) - }, vec![32, 1024, 1024 * 1024]); + group.bench_function( + BenchmarkId::new("signing", format!("{}", msg_size)), + |b| b.iter(|| key.sign(&msg)), + ); + } - c.bench_function_over_inputs("verifying - ed25519", |b, &msg_size| { + for msg_size in vec![32, 1024, 1024 * 1024] { let msg = (0..msg_size) .map(|_| rand::random::()) .collect::>(); let key = sp_core::ed25519::Pair::generate().0; let sig = key.sign(&msg); let public = key.public(); - b.iter(|| sp_core::ed25519::Pair::verify(&sig, &msg, &public)) - }, vec![32, 1024, 1024 * 1024]); + group.bench_function( + BenchmarkId::new("verifying", format!("{}", msg_size)), + |b| b.iter(|| sp_core::ed25519::Pair::verify(&sig, &msg, &public)), + ); + } + + group.finish(); } fn bench_sr25519(c: &mut Criterion) { - c.bench_function_over_inputs("signing - sr25519", |b, &msg_size| { + let mut group = c.benchmark_group("sr25519"); + + for msg_size in vec![32, 1024, 1024 * 1024] { let msg = (0..msg_size) .map(|_| rand::random::()) .collect::>(); let key = sp_core::sr25519::Pair::generate().0; - b.iter(|| key.sign(&msg)) - }, vec![32, 1024, 1024 * 1024]); + group.bench_function( + BenchmarkId::new("signing", format!("{}", msg_size)), + |b| b.iter(|| key.sign(&msg)), + ); + } - c.bench_function_over_inputs("verifying - sr25519", |b, &msg_size| { + for msg_size in vec![32, 1024, 1024 * 1024] { let msg = (0..msg_size) .map(|_| rand::random::()) .collect::>(); let key = sp_core::sr25519::Pair::generate().0; let sig = key.sign(&msg); let public = key.public(); - b.iter(|| sp_core::sr25519::Pair::verify(&sig, &msg, &public)) - }, vec![32, 1024, 1024 * 1024]); + group.bench_function( + BenchmarkId::new("verifying", format!("{}", msg_size)), + |b| b.iter(|| sp_core::sr25519::Pair::verify(&sig, &msg, &public)), + ); + } + + group.finish(); } fn bench_ecdsa(c: &mut Criterion) { - c.bench_function_over_inputs("signing - ecdsa", |b, &msg_size| { + let mut group = c.benchmark_group("ecdsa"); + + for msg_size in vec![32, 1024, 1024 * 1024] { let msg = (0..msg_size) .map(|_| rand::random::()) .collect::>(); let key = sp_core::ecdsa::Pair::generate().0; - b.iter(|| key.sign(&msg)) - }, vec![32, 1024, 1024 * 1024]); + group.bench_function( + BenchmarkId::new("signing", format!("{}", msg_size)), + |b| b.iter(|| key.sign(&msg)), + ); + } - c.bench_function_over_inputs("verifying - ecdsa", |b, &msg_size| { + for msg_size in vec![32, 1024, 1024 * 1024] { let msg = (0..msg_size) .map(|_| rand::random::()) .collect::>(); let key = sp_core::ecdsa::Pair::generate().0; let sig = key.sign(&msg); let public = key.public(); - b.iter(|| sp_core::ecdsa::Pair::verify(&sig, &msg, &public)) - }, vec![32, 1024, 1024 * 1024]); -} + group.bench_function( + BenchmarkId::new("verifying", format!("{}", msg_size)), + |b| b.iter(|| sp_core::ecdsa::Pair::verify(&sig, &msg, &public)), + ); + } -criterion_group!{ - name = benches; - config = Criterion::default().warm_up_time(Duration::from_millis(500)).without_plots(); - targets = bench_hash_128_fix_size, bench_hash_128_dyn_size, bench_ed25519, bench_sr25519, bench_ecdsa + group.finish(); } + +criterion_group!( + benches, + bench_hash_128_fix_size, + bench_hash_128_dyn_size, + bench_ed25519, + bench_sr25519, + bench_ecdsa, +); criterion_main!(benches); diff --git a/primitives/externalities/Cargo.toml b/primitives/externalities/Cargo.toml index 05de1837dc1d5..8552f50ec71be 100644 --- a/primitives/externalities/Cargo.toml +++ b/primitives/externalities/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-storage = { version = "3.0.0", path = "../storage", default-features = false } sp-std = { version = "3.0.0", path = "../std", default-features = false } -environmental = { version = "1.1.2", default-features = false } +environmental = { version = "1.1.3", default-features = false } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } [features] diff --git a/primitives/externalities/src/lib.rs b/primitives/externalities/src/lib.rs index 1077f41048d59..e7958c4e80573 100644 --- a/primitives/externalities/src/lib.rs +++ b/primitives/externalities/src/lib.rs @@ -302,7 +302,7 @@ pub trait ExternalitiesExt { impl ExternalitiesExt for &mut dyn Externalities { fn extension(&mut self) -> Option<&mut T> { - self.extension_by_type_id(TypeId::of::()).and_then(Any::downcast_mut) + self.extension_by_type_id(TypeId::of::()).and_then(::downcast_mut) } fn register_extension(&mut self, ext: T) -> Result<(), Error> { diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 0212d1bd8f2f7..ab72ecda042c0 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -159,13 +159,7 @@ pub trait OnOffenceHandler { offenders: &[OffenceDetails], slash_fraction: &[Perbill], session: SessionIndex, - ) -> Result; - - /// Can an offence be reported now or not. This is an method to short-circuit a call into - /// `on_offence`. Ideally, a correct implementation should return `false` if `on_offence` will - /// return `Err`. Nonetheless, this is up to the implementation and this trait cannot guarantee - /// it. - fn can_report() -> bool; + ) -> Res; } impl OnOffenceHandler for () { @@ -173,11 +167,9 @@ impl OnOffenceHandler _offenders: &[OffenceDetails], _slash_fraction: &[Perbill], _session: SessionIndex, - ) -> Result { - Ok(Default::default()) + ) -> Res { + Default::default() } - - fn can_report() -> bool { true } } /// A details about an offending authority for a particular kind of offence. diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index b8bee6380006a..dedeab60a3edc 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -13,10 +13,10 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee-http-client = { version = "0.2.0-alpha", default-features = false, features = ["tokio02"] } +jsonrpsee-http-client = { version = "=0.2.0-alpha.2", default-features = false, features = ["tokio02"] } # Needed by jsonrpsee-proc-macros: https://github.com/paritytech/jsonrpsee/issues/214 -jsonrpsee-types = "0.2.0-alpha.2" -jsonrpsee-proc-macros = "0.2.0-alpha.2" +jsonrpsee-types = "=0.2.0-alpha.2" +jsonrpsee-proc-macros = "=0.2.0-alpha.2" hex-literal = "0.3.1" env_logger = "0.8.2"