mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 17:31:03 +00:00
elections-phragmen: renounce candidacy [+ remove dead code] (#4123)
* new elections phragmen module. * Bump * rename typo * Few nits * Fix runner outgoing bond * remove useused var * Fix refund logic. * Update doc. * Update lock
This commit is contained in:
Generated
+200
-200
@@ -1263,6 +1263,181 @@ dependencies = [
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-executive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-indices 2.0.0",
|
||||
"pallet-transaction-payment 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-metadata"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-metadata 2.0.0",
|
||||
"frame-support-procedural 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-arithmetic 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-inherents 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-state-machine 2.0.0",
|
||||
"tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-procedural"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support-procedural-tools 2.0.0",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-procedural-tools"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support-procedural-tools-derive 2.0.0",
|
||||
"proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-procedural-tools-derive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-rpc"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-client-transports 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-primitives-storage 2.0.0",
|
||||
"substrate-rpc-api 2.0.0",
|
||||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-test"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-inherents 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-state-machine 2.0.0",
|
||||
"trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-system"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"sr-version 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-system-rpc"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-system-rpc-runtime-api 2.0.0",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core-client 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-derive 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-test-runtime-client 2.0.0",
|
||||
"substrate-transaction-pool 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-system-rpc-runtime-api"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-api 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-utility"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-swap"
|
||||
version = "0.2.4"
|
||||
@@ -2851,6 +3026,8 @@ dependencies = [
|
||||
"console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2863,8 +3040,6 @@ dependencies = [
|
||||
"node-primitives 2.0.0",
|
||||
"node-rpc 2.0.0",
|
||||
"node-runtime 2.0.0",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-authority-discovery 0.1.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-contracts 2.0.0",
|
||||
@@ -2919,11 +3094,11 @@ name = "node-executor"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"node-primitives 2.0.0",
|
||||
"node-runtime 2.0.0",
|
||||
"node-testing 2.0.0",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-contracts 2.0.0",
|
||||
"pallet-grandpa 2.0.0",
|
||||
@@ -2958,10 +3133,10 @@ dependencies = [
|
||||
name = "node-rpc"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-system-rpc 2.0.0",
|
||||
"jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-primitives 2.0.0",
|
||||
"node-runtime 2.0.0",
|
||||
"frame-system-rpc 2.0.0",
|
||||
"pallet-contracts-rpc 2.0.0",
|
||||
"pallet-transaction-payment-rpc 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
@@ -2986,13 +3161,13 @@ dependencies = [
|
||||
name = "node-runtime"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-primitives 2.0.0",
|
||||
"frame-executive 2.0.0",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"frame-system-rpc-runtime-api 2.0.0",
|
||||
"frame-utility 2.0.0",
|
||||
"integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-primitives 2.0.0",
|
||||
"pallet-authority-discovery 0.1.0",
|
||||
"pallet-authorship 0.1.0",
|
||||
"pallet-babe 2.0.0",
|
||||
@@ -3110,11 +3285,11 @@ dependencies = [
|
||||
name = "node-testing"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"node-executor 2.0.0",
|
||||
"node-primitives 2.0.0",
|
||||
"node-runtime 2.0.0",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-contracts 2.0.0",
|
||||
"pallet-grandpa 2.0.0",
|
||||
@@ -3279,181 +3454,6 @@ dependencies = [
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-executive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-indices 2.0.0",
|
||||
"pallet-transaction-payment 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-metadata"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-metadata 2.0.0",
|
||||
"frame-support-procedural 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-arithmetic 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-inherents 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-state-machine 2.0.0",
|
||||
"tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-procedural"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support-procedural-tools 2.0.0",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-procedural-tools"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support-procedural-tools-derive 2.0.0",
|
||||
"proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-procedural-tools-derive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-rpc"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-client-transports 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-primitives-storage 2.0.0",
|
||||
"substrate-rpc-api 2.0.0",
|
||||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-test"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-inherents 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-state-machine 2.0.0",
|
||||
"trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-system"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"sr-version 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-system-rpc"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core-client 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-derive 14.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-system-rpc-runtime-api 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-primitives 2.0.0",
|
||||
"substrate-client 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
"substrate-test-runtime-client 2.0.0",
|
||||
"substrate-transaction-pool 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-system-rpc-runtime-api"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-api 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-utility"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-io 2.0.0",
|
||||
"sr-primitives 2.0.0",
|
||||
"sr-std 2.0.0",
|
||||
"substrate-primitives 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-assets"
|
||||
version = "2.0.0"
|
||||
@@ -3472,9 +3472,9 @@ dependencies = [
|
||||
name = "pallet-aura"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-session 2.0.0",
|
||||
"pallet-timestamp 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3512,9 +3512,9 @@ dependencies = [
|
||||
name = "pallet-authorship"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-authorship 2.0.0",
|
||||
"sr-io 2.0.0",
|
||||
@@ -3528,10 +3528,10 @@ dependencies = [
|
||||
name = "pallet-babe"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-session 2.0.0",
|
||||
"pallet-timestamp 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3569,9 +3569,9 @@ dependencies = [
|
||||
name = "pallet-collective"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-balances 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3587,10 +3587,10 @@ name = "pallet-contracts"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-randomness-collective-flip 2.0.0",
|
||||
"pallet-timestamp 2.0.0",
|
||||
@@ -3654,9 +3654,9 @@ dependencies = [
|
||||
name = "pallet-elections"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-balances 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3671,9 +3671,9 @@ dependencies = [
|
||||
name = "pallet-elections-phragmen"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-balances 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3723,9 +3723,9 @@ dependencies = [
|
||||
name = "pallet-finality-tracker"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-finality-tracker 2.0.0",
|
||||
@@ -3881,10 +3881,10 @@ dependencies = [
|
||||
name = "pallet-session"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-timestamp 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3950,9 +3950,9 @@ dependencies = [
|
||||
name = "pallet-timestamp"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-timestamp 2.0.0",
|
||||
@@ -5474,11 +5474,11 @@ name = "subkey"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-system 2.0.0",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-primitives 2.0.0",
|
||||
"node-runtime 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"pallet-balances 2.0.0",
|
||||
"pallet-transaction-payment 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -6589,12 +6589,12 @@ name = "substrate-test-runtime"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memory-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"frame-executive 2.0.0",
|
||||
"frame-support 2.0.0",
|
||||
"frame-system 2.0.0",
|
||||
"frame-system-rpc-runtime-api 2.0.0",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memory-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pallet-babe 2.0.0",
|
||||
"pallet-timestamp 2.0.0",
|
||||
"parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
@@ -54,13 +54,19 @@
|
||||
//! back. A candidate can end up in one of the below situations:
|
||||
//! - **Winner**: A winner is kept as a _member_. They must still have a bond in reserve and they
|
||||
//! are automatically counted as a candidate for the next election.
|
||||
//! - **Loser**: Any of the candidate who are not a winner are left as losers. A loser might be an
|
||||
//! _outgoing member_, meaning that they are an active member who failed to keep their spot. In
|
||||
//! this case, the outgoing member will get their bond back. Otherwise, the bond is slashed from
|
||||
//! the loser candidate.
|
||||
//! - **Runner-up**: Runners-up are the best candidates immediately after the winners. The number
|
||||
//! of runners_up to keep is configurable. Runners-up are used, in order that they are elected,
|
||||
//! as replacements when a candidate is kicked by `remove_member()`.
|
||||
//! as replacements when a candidate is kicked by `[remove_member]`, or when an active member
|
||||
//! renounces their candidacy. Runners are automatically counted as a candidate for the next
|
||||
//! election.
|
||||
//! - **Loser**: Any of the candidate who are not a winner are left as losers. A loser might be an
|
||||
//! _outgoing member or runner_, meaning that they are an active member who failed to keep their
|
||||
//! spot. An outgoing will always lose their bond.
|
||||
//!
|
||||
//! ##### Renouncing candidacy.
|
||||
//!
|
||||
//! All candidates, elected or not, can renounce their candidacy. A call to [`renounce_candidacy`]
|
||||
//! will always cause the candidacy bond to be refunded.
|
||||
//!
|
||||
//! Note that with the members being the default candidates for the next round and votes persisting
|
||||
//! in storage, the election system is entirely stable given no further input. This means that if
|
||||
@@ -77,12 +83,10 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::Decode;
|
||||
use sr_primitives::{print, traits::{Zero, StaticLookup, Bounded, Convert}};
|
||||
use support::weights::SimpleDispatchInfo;
|
||||
use support::{
|
||||
decl_storage, decl_event, ensure, decl_module, dispatch,
|
||||
storage::unhashed,
|
||||
traits::{
|
||||
Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons,
|
||||
ChangeMembers, OnUnbalanced, WithdrawReason
|
||||
@@ -161,11 +165,6 @@ decl_storage! {
|
||||
/// The present candidate list. Sorted based on account id. A current member can never enter
|
||||
/// this vector and is always implicitly assumed to be a candidate.
|
||||
pub Candidates get(fn candidates): Vec<T::AccountId>;
|
||||
|
||||
/// Has the storage format been updated?
|
||||
/// NOTE: Only use and set to false if you have used an early version of this module. Should
|
||||
/// be set to true otherwise.
|
||||
DidMigrate: bool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +314,7 @@ decl_module! {
|
||||
let index = is_candidate.unwrap_err();
|
||||
|
||||
ensure!(!Self::is_member(&who), "member cannot re-submit candidacy");
|
||||
ensure!(!Self::is_runner(&who), "runner cannot re-submit candidacy");
|
||||
|
||||
T::Currency::reserve(&who, T::CandidacyBond::get())
|
||||
.map_err(|_| "candidate does not have enough funds")?;
|
||||
@@ -322,7 +322,63 @@ decl_module! {
|
||||
<Candidates<T>>::mutate(|c| c.insert(index, who));
|
||||
}
|
||||
|
||||
/// Remove a particular member from the set. This is effective immediately.
|
||||
/// Renounce one's intention to be a candidate for the next election round. 3 potential
|
||||
/// outcomes exist:
|
||||
/// - `origin` is a candidate and not elected in any set. In this case, the bond is
|
||||
/// unreserved, returned and origin is removed as a candidate.
|
||||
/// - `origin` is a current runner up. In this case, the bond is unreserved, returned and
|
||||
/// origin is removed as a runner.
|
||||
/// - `origin` is a current member. In this case, the bond is unreserved and origin is
|
||||
/// removed as a member, consequently not being a candidate for the next round anymore.
|
||||
/// Similar to [`remove_voter`], if replacement runners exists, they are immediately used.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(2_000_000)]
|
||||
fn renounce_candidacy(origin) {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
// NOTE: this function attempts the 3 conditions (being a candidate, member, runner) and
|
||||
// fails if none are matched. Unlike other Palette functions and modules where checks
|
||||
// happen first and then execution happens, this function is written the other way
|
||||
// around. The main intention is that reading all of the candidates, members and runners
|
||||
// from storage is expensive. Furthermore, we know (soft proof) that they are always
|
||||
// mutually exclusive. Hence, we try one, and only then decode more storage.
|
||||
|
||||
if let Ok(_replacement) = Self::remove_and_replace_member(&who) {
|
||||
T::Currency::unreserve(&who, T::CandidacyBond::get());
|
||||
Self::deposit_event(RawEvent::MemberRenounced(who.clone()));
|
||||
|
||||
// safety guard to make sure we do only one arm. Better to read runners later.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut runners_with_stake = Self::runners_up();
|
||||
if let Ok(index) = runners_with_stake
|
||||
.binary_search_by(|(ref r, ref _s)| r.cmp(&who))
|
||||
{
|
||||
runners_with_stake.remove(index);
|
||||
// unreserve the bond
|
||||
T::Currency::unreserve(&who, T::CandidacyBond::get());
|
||||
// update storage.
|
||||
<RunnersUp<T>>::put(runners_with_stake);
|
||||
// safety guard to make sure we do only one arm. Better to read runners later.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut candidates = Self::candidates();
|
||||
if let Ok(index) = candidates.binary_search(&who) {
|
||||
candidates.remove(index);
|
||||
// unreserve the bond
|
||||
T::Currency::unreserve(&who, T::CandidacyBond::get());
|
||||
// update storage.
|
||||
<Candidates<T>>::put(candidates);
|
||||
// safety guard to make sure we do only one arm. Better to read runners later.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return Err("origin is not a candidate, member or a runner.");
|
||||
}
|
||||
|
||||
/// Remove a particular member from the set. This is effective immediately and the bond of
|
||||
/// the outgoing member is slashed.
|
||||
///
|
||||
/// If a runner-up is available, then the best runner-up will be removed and replaces the
|
||||
/// outgoing member. Otherwise, a new phragmen round is started.
|
||||
@@ -339,51 +395,21 @@ decl_module! {
|
||||
ensure_root(origin)?;
|
||||
let who = T::Lookup::lookup(who)?;
|
||||
|
||||
let mut members_with_stake = Self::members();
|
||||
if let Ok(index) = members_with_stake.binary_search_by(|(ref m, ref _s)| m.cmp(&who)) {
|
||||
// remove, slash, emit event.
|
||||
members_with_stake.remove(index);
|
||||
return Self::remove_and_replace_member(&who).map(|had_replacement| {
|
||||
let (imbalance, _) = T::Currency::slash_reserved(&who, T::CandidacyBond::get());
|
||||
T::KickedMember::on_unbalanced(imbalance);
|
||||
Self::deposit_event(RawEvent::MemberKicked(who.clone()));
|
||||
|
||||
let mut runners_up = Self::runners_up();
|
||||
if let Some((replacement, stake)) = runners_up.pop() {
|
||||
// replace the outgoing with the best runner up.
|
||||
if let Err(index) = members_with_stake
|
||||
.binary_search_by(|(ref m, ref _s)| m.cmp(&replacement))
|
||||
{
|
||||
members_with_stake.insert(index, (replacement.clone(), stake));
|
||||
ElectionRounds::mutate(|v| *v += 1);
|
||||
T::ChangeMembers::change_members_sorted(
|
||||
&[replacement],
|
||||
&[who],
|
||||
&members_with_stake
|
||||
.iter()
|
||||
.map(|(m, _)| m.clone())
|
||||
.collect::<Vec<T::AccountId>>(),
|
||||
);
|
||||
}
|
||||
// else it would mean that the runner up was already a member. This cannot
|
||||
// happen. If it does, not much that we can do about it.
|
||||
|
||||
<Members<T>>::put(members_with_stake);
|
||||
<RunnersUp<T>>::put(runners_up);
|
||||
} else {
|
||||
// update `Members` storage -- `do_phragmen` adds this to the candidate list.
|
||||
<Members<T>>::put(members_with_stake);
|
||||
// trigger a new phragmen. grab a cup of coffee. This might take a while.
|
||||
if !had_replacement {
|
||||
Self::do_phragmen();
|
||||
}
|
||||
}
|
||||
|
||||
()
|
||||
})
|
||||
}
|
||||
|
||||
/// What to do at the end of each block. Checks if an election needs to happen or not.
|
||||
fn on_initialize(n: T::BlockNumber) {
|
||||
if !DidMigrate::exists() {
|
||||
DidMigrate::put(true);
|
||||
Self::do_migrate();
|
||||
}
|
||||
if let Err(e) = Self::end_block(n) {
|
||||
print("Guru meditation");
|
||||
print(e);
|
||||
@@ -405,6 +431,8 @@ decl_event!(
|
||||
/// A member has been removed. This should always be followed by either `NewTerm` ot
|
||||
/// `EmptyTerm`.
|
||||
MemberKicked(AccountId),
|
||||
/// A member has renounced their candidacy.
|
||||
MemberRenounced(AccountId),
|
||||
/// A voter (first element) was reported (byt the second element) with the the report being
|
||||
/// successful or not (third element).
|
||||
VoterReported(AccountId, AccountId, bool),
|
||||
@@ -412,6 +440,54 @@ decl_event!(
|
||||
);
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Attempts to remove a member `who`. If a runner up exists, it is used as the replacement.
|
||||
/// Otherwise, `Ok(false)` is returned to signal the caller.
|
||||
///
|
||||
/// In both cases, [`Members`], [`ElectionRounds`] and [`RunnersUp`] storage are updated
|
||||
/// accordingly. Furthermore, the membership change is reported.
|
||||
///
|
||||
/// O(phragmen) in the worse case.
|
||||
fn remove_and_replace_member(who: &T::AccountId) -> Result<bool, &'static str> {
|
||||
let mut members_with_stake = Self::members();
|
||||
if let Ok(index) = members_with_stake.binary_search_by(|(ref m, ref _s)| m.cmp(who)) {
|
||||
members_with_stake.remove(index);
|
||||
|
||||
let mut runners_up = Self::runners_up();
|
||||
if let Some((replacement, stake)) = runners_up.pop() {
|
||||
// replace the outgoing with the best runner up.
|
||||
if let Err(index) = members_with_stake
|
||||
.binary_search_by(|(ref m, ref _s)| m.cmp(&replacement))
|
||||
{
|
||||
members_with_stake.insert(index, (replacement.clone(), stake));
|
||||
ElectionRounds::mutate(|v| *v += 1);
|
||||
T::ChangeMembers::change_members_sorted(
|
||||
&[replacement],
|
||||
&[who.clone()],
|
||||
&members_with_stake
|
||||
.iter()
|
||||
.map(|(m, _)| m.clone())
|
||||
.collect::<Vec<T::AccountId>>(),
|
||||
);
|
||||
}
|
||||
// else it would mean that the runner up was already a member. This cannot
|
||||
// happen. If it does, not much that we can do about it.
|
||||
|
||||
<Members<T>>::put(members_with_stake);
|
||||
<RunnersUp<T>>::put(runners_up);
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
// update `Members` storage -- `do_phragmen` adds this to the candidate list.
|
||||
<Members<T>>::put(members_with_stake);
|
||||
|
||||
// signal caller that no replacement has been found.
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Err("not a member")
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if `who` is a candidate. It returns the insert index if the element does not exists as
|
||||
/// an error.
|
||||
///
|
||||
@@ -434,6 +510,13 @@ impl<T: Trait> Module<T> {
|
||||
Self::members_ids().binary_search(who).is_ok()
|
||||
}
|
||||
|
||||
/// Check if `who` is currently an active runner.
|
||||
///
|
||||
/// Limited number of runners-up. Binary search. Constant time factor. O(1)
|
||||
fn is_runner(who: &T::AccountId) -> bool {
|
||||
Self::runners_up_ids().binary_search(who).is_ok()
|
||||
}
|
||||
|
||||
/// Returns number of desired members.
|
||||
fn desired_members() -> u32 {
|
||||
T::DesiredMembers::get()
|
||||
@@ -524,14 +607,12 @@ impl<T: Trait> Module<T> {
|
||||
let mut candidates = Self::candidates();
|
||||
// candidates who explicitly called `submit_candidacy`. Only these folks are at the risk of
|
||||
// losing their bond.
|
||||
let mut exposed_candidates = candidates.clone();
|
||||
let exposed_candidates = candidates.clone();
|
||||
// current members are always a candidate for the next round as well.
|
||||
// this is guaranteed to not create any duplicates.
|
||||
candidates.append(&mut Self::members_ids());
|
||||
// previous runners_up are also always candidates for the next round.
|
||||
candidates.append(&mut Self::runners_up_ids());
|
||||
// and exposed to being an outgoing in case they are no longer elected.
|
||||
exposed_candidates.append(&mut Self::runners_up_ids());
|
||||
|
||||
let voters_and_votes = <VotesOf<T>>::enumerate()
|
||||
.map(|(v, i)| (v, i))
|
||||
@@ -544,9 +625,10 @@ impl<T: Trait> Module<T> {
|
||||
Self::locked_stake_of,
|
||||
);
|
||||
|
||||
let mut to_release_bond: Vec<T::AccountId> = Vec::with_capacity(desired_seats);
|
||||
let old_members = <Members<T>>::take();
|
||||
if let Some(phragmen_result) = maybe_phragmen_result {
|
||||
let old_members = <Members<T>>::take();
|
||||
let old_runners = <RunnersUp<T>>::take();
|
||||
|
||||
// filter out those who had literally no votes at all.
|
||||
// AUDIT/NOTE: the need to do this is because all candidates, even those who have no
|
||||
// vote are still considered by phragmen and when good candidates are scarce, then these
|
||||
@@ -581,22 +663,28 @@ impl<T: Trait> Module<T> {
|
||||
// split new set into winners and runner ups.
|
||||
let split_point = desired_seats.min(new_set_with_stake.len());
|
||||
let mut new_members = (&new_set_with_stake[..split_point]).to_vec();
|
||||
let runners_up = &new_set_with_stake[split_point..]
|
||||
let new_members_ids = new_members
|
||||
.iter()
|
||||
.map(|(m, _)| m.clone())
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
let new_runners_up = &new_set_with_stake[split_point..]
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.rev()
|
||||
.collect::<Vec<(T::AccountId, BalanceOf<T>)>>();
|
||||
let new_runners_up_ids = new_runners_up
|
||||
.iter()
|
||||
.map(|(r, _)| r.clone())
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
|
||||
|
||||
// save the runners as-is. They are sorted based on desirability.
|
||||
// sort and save the members.
|
||||
new_members.sort();
|
||||
<Members<T>>::put(&new_members);
|
||||
|
||||
// save the runners as-is
|
||||
<RunnersUp<T>>::put(runners_up);
|
||||
|
||||
// report member changes. We compute diff because we need the outgoing list.
|
||||
let (incoming, outgoing) = T::ChangeMembers::compute_members_diff(
|
||||
&Self::members_ids(),
|
||||
&new_members_ids,
|
||||
&old_members.into_iter().map(|(m, _)| m).collect::<Vec<T::AccountId>>(),
|
||||
);
|
||||
T::ChangeMembers::change_members_sorted(
|
||||
@@ -605,62 +693,50 @@ impl<T: Trait> Module<T> {
|
||||
&Self::members_ids(),
|
||||
);
|
||||
|
||||
// unlike exposed_candidates, these are members who were in the list and no longer
|
||||
// exist. They must get their bond back.
|
||||
to_release_bond = outgoing.to_vec();
|
||||
// outgoing candidates lose their bond.
|
||||
let mut to_burn_bond = outgoing.to_vec();
|
||||
|
||||
// compute the outgoing of runners up as well and append them to the `to_burn_bond`
|
||||
{
|
||||
let (_, outgoing) = T::ChangeMembers::compute_members_diff(
|
||||
&new_runners_up_ids,
|
||||
&old_runners.into_iter().map(|(r, _)| r).collect::<Vec<T::AccountId>>(),
|
||||
);
|
||||
to_burn_bond.extend(outgoing);
|
||||
}
|
||||
|
||||
// Burn loser bond. members list is sorted. O(NLogM) (N candidates, M members)
|
||||
// runner up list is not sorted. O(K*N) given K runner ups. Overall: O(NLogM + N*K)
|
||||
// both the member and runner counts are bounded.
|
||||
exposed_candidates.into_iter().for_each(|c| {
|
||||
// any candidate who is not a member and not a runner up.
|
||||
if new_members.binary_search_by_key(&c, |(m, _)| m.clone()).is_err()
|
||||
&& !Self::runners_up_ids().contains(&c)
|
||||
&& !new_runners_up_ids.contains(&c)
|
||||
{
|
||||
let (imbalance, _) = T::Currency::slash_reserved(&c, T::CandidacyBond::get());
|
||||
T::LoserCandidate::on_unbalanced(imbalance);
|
||||
}
|
||||
});
|
||||
Self::deposit_event(RawEvent::NewTerm(new_members.to_vec()));
|
||||
|
||||
// Burn outgoing bonds
|
||||
to_burn_bond.into_iter().for_each(|x| {
|
||||
let (imbalance, _) = T::Currency::slash_reserved(&x, T::CandidacyBond::get());
|
||||
T::LoserCandidate::on_unbalanced(imbalance);
|
||||
});
|
||||
|
||||
<Members<T>>::put(&new_members);
|
||||
<RunnersUp<T>>::put(new_runners_up);
|
||||
|
||||
Self::deposit_event(RawEvent::NewTerm(new_members.clone().to_vec()));
|
||||
} else {
|
||||
Self::deposit_event(RawEvent::EmptyTerm);
|
||||
}
|
||||
|
||||
// unreserve the bond of all the outgoings.
|
||||
to_release_bond.iter().for_each(|m| {
|
||||
T::Currency::unreserve(&m, T::CandidacyBond::get());
|
||||
});
|
||||
|
||||
// clean candidates.
|
||||
<Candidates<T>>::kill();
|
||||
|
||||
ElectionRounds::mutate(|v| *v += 1);
|
||||
}
|
||||
|
||||
/// Perform the storage update needed to migrate the module from the initial version of the
|
||||
/// storage.
|
||||
///
|
||||
/// If decoding the old storage fails in any way, the consequence is that we start with an empty
|
||||
/// set.
|
||||
fn do_migrate() {
|
||||
// old storage format.
|
||||
let old_members: Vec<T::AccountId> = unhashed::get_raw(&<Members<T>>::hashed_key())
|
||||
.and_then(|bytes| Decode::decode(&mut &*bytes).ok()).unwrap_or_default();
|
||||
let old_runners: Vec<T::AccountId> = unhashed::get_raw(&<RunnersUp<T>>::hashed_key())
|
||||
.and_then(|bytes| Decode::decode(&mut &*bytes).ok()).unwrap_or_default();
|
||||
|
||||
// new storage format.
|
||||
let new_runners: Vec<(T::AccountId, BalanceOf<T>)> = old_runners
|
||||
.into_iter()
|
||||
.map(|r| (r, Zero::zero()))
|
||||
.collect();
|
||||
let new_members: Vec<(T::AccountId, BalanceOf<T>)> = old_members
|
||||
.into_iter()
|
||||
.map(|r| (r, Zero::zero()))
|
||||
.collect();
|
||||
|
||||
<Members<T>>::put(new_members);
|
||||
<RunnersUp<T>>::put(new_runners);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -671,7 +747,7 @@ mod tests {
|
||||
use primitives::H256;
|
||||
use sr_primitives::{
|
||||
Perbill, testing::Header, BuildStorage,
|
||||
traits::{OnInitialize, BlakeTwo256, IdentityLookup, Block as BlockT},
|
||||
traits::{BlakeTwo256, IdentityLookup, Block as BlockT},
|
||||
};
|
||||
use crate as elections;
|
||||
|
||||
@@ -860,30 +936,6 @@ mod tests {
|
||||
lock.amount
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temp_migration_works() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
use support::storage::unhashed;
|
||||
use codec::Encode;
|
||||
|
||||
let old_members = vec![1u64, 2];
|
||||
let old_runners = vec![3u64];
|
||||
|
||||
let members_key = <Members<Test>>::hashed_key();
|
||||
let runners_key = <RunnersUp<Test>>::hashed_key();
|
||||
|
||||
unhashed::put_raw(&members_key, &old_members.encode()[..]);
|
||||
unhashed::put_raw(&runners_key, &old_runners.encode()[..]);
|
||||
|
||||
assert_eq!(DidMigrate::get(), false);
|
||||
<Elections as OnInitialize<u64>>::on_initialize(1);
|
||||
assert_eq!(DidMigrate::get(), true);
|
||||
|
||||
assert_eq!(Elections::members(), vec![(1, 0), (2, 0)]);
|
||||
assert_eq!(Elections::runners_up(), vec![(3, 0)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn params_should_work() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
@@ -1017,13 +1069,36 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runner_candidate_submission_should_not_work() {
|
||||
ExtBuilder::default().desired_runners_up(2).build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(2), vec![5, 4], 20));
|
||||
assert_ok!(Elections::vote(Origin::signed(1), vec![3], 10));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![3]);
|
||||
|
||||
assert_noop!(
|
||||
Elections::submit_candidacy(Origin::signed(3)),
|
||||
"runner cannot re-submit candidacy",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poor_candidate_submission_should_not_work() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_eq!(Elections::candidates(), Vec::<u64>::new());
|
||||
assert_noop!(
|
||||
Elections::submit_candidacy(Origin::signed(7)),
|
||||
"candidate does not have enough funds"
|
||||
"candidate does not have enough funds",
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1089,7 +1164,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_vote_for_old_members_even_when_no_new_candidates() {
|
||||
// let allowed_votes = candidates_count as usize + Self::members().len()
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
@@ -1114,7 +1188,7 @@ mod tests {
|
||||
|
||||
assert_noop!(
|
||||
Elections::vote(Origin::signed(2), vec![10, 20, 30], 20),
|
||||
"cannot vote more than candidates"
|
||||
"cannot vote more than candidates",
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1127,7 +1201,7 @@ mod tests {
|
||||
|
||||
assert_noop!(
|
||||
Elections::vote(Origin::signed(2), vec![4], 1),
|
||||
"cannot vote with stake less than minimum balance"
|
||||
"cannot vote with stake less than minimum balance",
|
||||
);
|
||||
})
|
||||
}
|
||||
@@ -1484,7 +1558,6 @@ mod tests {
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
|
||||
assert_eq!(Elections::runners_up_ids(), vec![2]);
|
||||
assert_eq!(balances(&2), (15, 5));
|
||||
|
||||
@@ -1493,16 +1566,63 @@ mod tests {
|
||||
|
||||
System::set_block_number(10);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
|
||||
assert_eq!(Elections::runners_up_ids(), vec![3]);
|
||||
assert_eq!(balances(&3), (25, 5));
|
||||
assert_eq!(balances(&2), (15, 2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_members_are_always_implicitly_next_candidate() {
|
||||
fn members_lose_bond_once_outgoing() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_eq!(balances(&5), (50, 0));
|
||||
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(5), vec![5], 50));
|
||||
assert_eq!(balances(&5), (45, 5));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
assert_eq!(Elections::members_ids(), vec![5]);
|
||||
|
||||
assert_ok!(Elections::remove_voter(Origin::signed(5)));
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
|
||||
System::set_block_number(10);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
assert_eq!(Elections::members_ids(), vec![]);
|
||||
|
||||
assert_eq!(balances(&5), (47, 0));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn losers_will_lose_the_bond() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(4), vec![5], 40));
|
||||
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
assert_eq!(balances(&3), (27, 3));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![5]);
|
||||
|
||||
// winner
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
// loser
|
||||
assert_eq!(balances(&3), (27, 0));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_members_are_always_next_candidate() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
@@ -1630,55 +1750,6 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outgoing_will_get_the_bond_back() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_eq!(balances(&5), (50, 0));
|
||||
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(5), vec![5], 50));
|
||||
assert_eq!(balances(&5), (45, 5));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
assert_eq!(Elections::members_ids(), vec![5]);
|
||||
|
||||
assert_ok!(Elections::remove_voter(Origin::signed(5)));
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
|
||||
System::set_block_number(10);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
assert_eq!(Elections::members_ids(), vec![]);
|
||||
|
||||
assert_eq!(balances(&5), (50, 0));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn losers_will_lose_the_bond() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(4), vec![5], 40));
|
||||
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
assert_eq!(balances(&3), (27, 3));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![5]);
|
||||
|
||||
// winner
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
// loser
|
||||
assert_eq!(balances(&3), (27, 0));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incoming_outgoing_are_reported() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
@@ -1717,8 +1788,8 @@ mod tests {
|
||||
// 1 is a loser, slashed by 3.
|
||||
assert_eq!(balances(&1), (5, 2));
|
||||
|
||||
// 5 is an outgoing loser, it will get their bond back.
|
||||
assert_eq!(balances(&5), (48, 2));
|
||||
// 5 is an outgoing loser. will also get slashed.
|
||||
assert_eq!(balances(&5), (45, 2));
|
||||
|
||||
assert_eq!(
|
||||
System::events()[0].event,
|
||||
@@ -1766,4 +1837,149 @@ mod tests {
|
||||
assert_eq!(Elections::runners_up(), vec![(3, 20), (2, 30)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn candidates_are_sorted() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
|
||||
assert_eq!(Elections::candidates(), vec![3, 5]);
|
||||
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
assert_ok!(Elections::renounce_candidacy(Origin::signed(3)));
|
||||
|
||||
assert_eq!(Elections::candidates(), vec![2, 4, 5]);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runner_up_replacement_maintains_members_order() {
|
||||
ExtBuilder::default().desired_runners_up(2).build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(2), vec![5], 20));
|
||||
assert_ok!(Elections::vote(Origin::signed(4), vec![4], 40));
|
||||
assert_ok!(Elections::vote(Origin::signed(5), vec![2], 50));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![2, 4]);
|
||||
assert_ok!(Elections::remove_member(Origin::ROOT, 2));
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_renounce_candidacy_member_with_runners_bond_is_refunded() {
|
||||
ExtBuilder::default().desired_runners_up(2).build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(5), vec![5], 50));
|
||||
assert_ok!(Elections::vote(Origin::signed(4), vec![4], 40));
|
||||
assert_ok!(Elections::vote(Origin::signed(3), vec![3], 30));
|
||||
assert_ok!(Elections::vote(Origin::signed(2), vec![2], 20));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![2, 3]);
|
||||
|
||||
assert_ok!(Elections::renounce_candidacy(Origin::signed(4)));
|
||||
assert_eq!(balances(&4), (38, 2)); // 2 is voting bond.
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![3, 5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![2]);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_renounce_candidacy_member_without_runners_bond_is_refunded() {
|
||||
ExtBuilder::default().desired_runners_up(2).build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(5), vec![5], 50));
|
||||
assert_ok!(Elections::vote(Origin::signed(4), vec![4], 40));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
|
||||
assert_ok!(Elections::vote(Origin::signed(3), vec![3], 30));
|
||||
assert_ok!(Elections::vote(Origin::signed(2), vec![2], 20));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![]);
|
||||
assert_eq!(Elections::candidates(), vec![2, 3]);
|
||||
|
||||
assert_ok!(Elections::renounce_candidacy(Origin::signed(4)));
|
||||
assert_eq!(balances(&4), (38, 2)); // 2 is voting bond.
|
||||
|
||||
// no replacement
|
||||
assert_eq!(Elections::members_ids(), vec![5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![]);
|
||||
// still candidate
|
||||
assert_eq!(Elections::candidates(), vec![2, 3]);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_renounce_candidacy_runner() {
|
||||
ExtBuilder::default().desired_runners_up(2).build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
|
||||
|
||||
assert_ok!(Elections::vote(Origin::signed(5), vec![4], 50));
|
||||
assert_ok!(Elections::vote(Origin::signed(4), vec![5], 40));
|
||||
assert_ok!(Elections::vote(Origin::signed(3), vec![3], 30));
|
||||
assert_ok!(Elections::vote(Origin::signed(2), vec![2], 20));
|
||||
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Elections::end_block(System::block_number()));
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![2, 3]);
|
||||
|
||||
assert_ok!(Elections::renounce_candidacy(Origin::signed(3)));
|
||||
assert_eq!(balances(&3), (28, 2)); // 2 is voting bond.
|
||||
|
||||
assert_eq!(Elections::members_ids(), vec![4, 5]);
|
||||
assert_eq!(Elections::runners_up_ids(), vec![2]);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_renounce_candidacy_candidate() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
|
||||
assert_eq!(balances(&5), (47, 3));
|
||||
assert_eq!(Elections::candidates(), vec![5]);
|
||||
|
||||
assert_ok!(Elections::renounce_candidacy(Origin::signed(5)));
|
||||
assert_eq!(balances(&5), (50, 0));
|
||||
assert_eq!(Elections::candidates(), vec![]);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrong_renounce_candidacy_should_fail() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_noop!(
|
||||
Elections::renounce_candidacy(Origin::signed(5)),
|
||||
"origin is not a candidate, member or a runner.",
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user