diff --git a/Cargo.lock b/Cargo.lock index 045b2780c4bf6..a40913bdf3bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4231,6 +4231,7 @@ dependencies = [ "sp-inherents", "sp-io", "sp-keyring", + "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-session", @@ -5329,7 +5330,6 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-io", - "sp-npos-elections", "sp-runtime", "sp-staking", "sp-std", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 43ecca7e74456..f0cad60f2614d 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -36,6 +36,7 @@ sp-keyring = { version = "3.0.0", optional = true, path = "../../../primitives/k sp-session = { version = "3.0.0", default-features = false, path = "../../../primitives/session" } sp-transaction-pool = { version = "3.0.0", default-features = false, path = "../../../primitives/transaction-pool" } sp-version = { version = "3.0.0", default-features = false, path = "../../../primitives/version" } +sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../../primitives/npos-elections" } # frame dependencies frame-executive = { version = "3.0.0", default-features = false, path = "../../../frame/executive" } @@ -159,6 +160,7 @@ std = [ "pallet-vesting/std", "log/std", "frame-try-runtime/std", + "sp-npos-elections/std", ] runtime-benchmarks = [ "frame-benchmarking", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 15ae0209a5e25..4fc8f4e20b5bd 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -471,6 +471,8 @@ parameter_types! { } impl pallet_staking::Config for Runtime { + const MAX_NOMINATIONS: u32 = + ::LIMIT as u32; type Currency = Balances; type UnixTime = Timestamp; type CurrencyToVote = U128CurrencyToVote; @@ -515,6 +517,12 @@ parameter_types! { .saturating_sub(BlockExecutionWeight::get()); } +sp_npos_elections::generate_solution_type!( + #[compact] + pub struct NposCompactSolution16::(16) + // -------------------- ^^ +); + impl pallet_election_provider_multi_phase::Config for Runtime { type Event = Event; type Currency = Balances; @@ -526,7 +534,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type MinerTxPriority = MultiPhaseUnsignedPriority; type DataProvider = Staking; type OnChainAccuracy = Perbill; - type CompactSolution = pallet_staking::CompactAssignments; + type CompactSolution = NposCompactSolution16; type Fallback = Fallback; type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight; type BenchmarkingConfig = (); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 93b4af00b5dc3..137f32b5e502c 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -193,6 +193,7 @@ impl onchain::Config for Test { } impl pallet_staking::Config for Test { + const MAX_NOMINATIONS: u32 = 16; type RewardRemainder = (); type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type Event = Event; diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 641807294f05c..1609ffa3beef0 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -678,6 +678,16 @@ pub mod pallet { let _: UpperOf> = maximum_chain_accuracy .iter() .fold(Zero::zero(), |acc, x| acc.checked_add(x).unwrap()); + + // We only accept data provider who's maximum votes per voter matches our + // `T::CompactSolution`'s `LIMIT`. + // + // NOTE that this pallet does not really need to enforce this in runtime. The compact + // solution cannot represent any voters more than `LIMIT` anyhow. + assert_eq!( + >::MAXIMUM_VOTES_PER_VOTER, + as CompactSolution>::LIMIT as u32, + ); } } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 67e77db296c3e..22b5a0ac67b7a 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -300,6 +300,7 @@ pub struct ExtBuilder {} pub struct StakingMock; impl ElectionDataProvider for StakingMock { + const MAXIMUM_VOTES_PER_VOTER: u32 = ::LIMIT as u32; fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { let targets = Targets::get(); diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 1a4a293a3270d..b846460e71f8a 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -100,6 +100,7 @@ //! pub struct Module(std::marker::PhantomData); //! //! impl ElectionDataProvider for Module { +//! const MAXIMUM_VOTES_PER_VOTER: u32 = 1; //! fn desired_targets() -> data_provider::Result<(u32, Weight)> { //! Ok((1, 0)) //! } @@ -180,6 +181,9 @@ pub mod data_provider { /// Something that can provide the data to an [`ElectionProvider`]. pub trait ElectionDataProvider { + /// Maximum number of votes per voter that this data provider is providing. + const MAXIMUM_VOTES_PER_VOTER: u32; + /// All possible targets for the election, i.e. the candidates. /// /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items @@ -226,6 +230,7 @@ pub trait ElectionDataProvider { #[cfg(feature = "std")] impl ElectionDataProvider for () { + const MAXIMUM_VOTES_PER_VOTER: u32 = 0; fn targets(_maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { Ok(Default::default()) } diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index b00c8698037c1..e034a9c36a8ac 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -125,6 +125,7 @@ mod tests { pub struct DataProvider; impl ElectionDataProvider for DataProvider { + const MAXIMUM_VOTES_PER_VOTER: u32 = 2; fn voters( _: Option, ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index af9d7f0fe425c..3f450e18bc783 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -199,6 +199,7 @@ impl onchain::Config for Test { } impl pallet_staking::Config for Test { + const MAX_NOMINATIONS: u32 = 16; type RewardRemainder = (); type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type Event = Event; diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 83275da593e90..08517a4ac8df0 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -28,7 +28,10 @@ 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 sp_runtime::{Perbill, traits::{Convert, StaticLookup, Saturating, UniqueSaturatedInto}}; +use sp_runtime::{ + Perbill, + traits::{Convert, StaticLookup, Saturating, UniqueSaturatedInto}, +}; use sp_staking::offence::{ReportOffence, Offence, OffenceDetails}; use pallet_balances::Config as BalancesConfig; @@ -39,8 +42,8 @@ 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, IndividualExposure, MAX_NOMINATIONS, Event as StakingEvent + Module as Staking, Config as StakingConfig, RewardDestination, ValidatorPrefs, Exposure, + IndividualExposure, Event as StakingEvent, }; const SEED: u32 = 0; @@ -236,7 +239,7 @@ benchmarks! { let r in 1 .. MAX_REPORTERS; // we skip 1 offender, because in such case there is no slashing let o in 2 .. MAX_OFFENDERS; - let n in 0 .. MAX_NOMINATORS.min(MAX_NOMINATIONS as u32); + let n in 0 .. MAX_NOMINATORS.min(::MAX_NOMINATIONS); // Make r reporters let mut reporters = vec![]; @@ -310,7 +313,7 @@ benchmarks! { } report_offence_grandpa { - let n in 0 .. MAX_NOMINATORS.min(MAX_NOMINATIONS as u32); + let n in 0 .. MAX_NOMINATORS.min(::MAX_NOMINATIONS); // for grandpa equivocation reports the number of reporters // and offenders is always 1 @@ -346,7 +349,7 @@ benchmarks! { } report_offence_babe { - let n in 0 .. MAX_NOMINATORS.min(MAX_NOMINATIONS as u32); + let n in 0 .. MAX_NOMINATORS.min(::MAX_NOMINATIONS); // for babe equivocation reports the number of reporters // and offenders is always 1 diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 1fe8db5aaaa29..223d6d4d477a6 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -158,6 +158,7 @@ impl onchain::Config for Test { } impl pallet_staking::Config for Test { + const MAX_NOMINATIONS: u32 = 16; type Currency = Balances; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index 696a86166c1d7..fff3717607f8f 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -35,7 +35,7 @@ use frame_system::RawOrigin; use pallet_session::{historical::Module as Historical, Module as Session, *}; use pallet_staking::{ benchmarking::create_validator_with_nominators, testing_utils::create_validators, - MAX_NOMINATIONS, RewardDestination, + RewardDestination, }; use sp_runtime::traits::{One, StaticLookup}; @@ -52,10 +52,10 @@ impl OnInitialize for Pallet { benchmarks! { set_keys { - let n = MAX_NOMINATIONS as u32; + let n = ::MAX_NOMINATIONS; let (v_stash, _) = create_validator_with_nominators::( n, - MAX_NOMINATIONS as u32, + ::MAX_NOMINATIONS, false, RewardDestination::Staked, )?; @@ -68,10 +68,10 @@ benchmarks! { }: _(RawOrigin::Signed(v_controller), keys, proof) purge_keys { - let n = MAX_NOMINATIONS as u32; + let n = ::MAX_NOMINATIONS; let (v_stash, _) = create_validator_with_nominators::( n, - MAX_NOMINATIONS as u32, + ::MAX_NOMINATIONS, false, RewardDestination::Staked )?; diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index af5d8f6a0936b..53afeb620c260 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -163,6 +163,7 @@ impl onchain::Config for Test { } impl pallet_staking::Config for Test { + const MAX_NOMINATIONS: u32 = 16; type Currency = Balances; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 24909b35f53cf..908e361e667e3 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -17,7 +17,6 @@ static_assertions = "1.1.0" serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } -sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } sp-io ={ version = "3.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" } sp-staking = { version = "3.0.0", default-features = false, path = "../../primitives/staking" } @@ -54,7 +53,6 @@ std = [ "serde", "codec/std", "sp-std/std", - "sp-npos-elections/std", "sp-io/std", "frame-support/std", "sp-runtime/std", diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 83a67abb3c8e1..1d8a5c1fd6451 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -204,16 +204,20 @@ benchmarks! { kick { // scenario: we want to kick `k` nominators from nominating us (we are a validator). // we'll assume that `k` is under 128 for the purposes of determining the slope. - // each nominator should have `MAX_NOMINATIONS` validators nominated, and our validator + // each nominator should have `T::MAX_NOMINATIONS` validators nominated, and our validator // should be somewhere in there. let k in 1 .. 128; - // these are the other validators; there are `MAX_NOMINATIONS - 1` of them, so there are a - // total of `MAX_NOMINATIONS` validators in the system. - let rest_of_validators = create_validators::(MAX_NOMINATIONS as u32 - 1, 100)?; + // these are the other validators; there are `T::MAX_NOMINATIONS - 1` of them, so + // there are a total of `T::MAX_NOMINATIONS` validators in the system. + let rest_of_validators = create_validators::(T::MAX_NOMINATIONS - 1, 100)?; // this is the validator that will be kicking. - let (stash, controller) = create_stash_controller::(MAX_NOMINATIONS as u32 - 1, 100, Default::default())?; + let (stash, controller) = create_stash_controller::( + T::MAX_NOMINATIONS - 1, + 100, + Default::default(), + )?; let stash_lookup: ::Source = T::Lookup::unlookup(stash.clone()); // they start validating. @@ -224,7 +228,11 @@ benchmarks! { let mut nominator_stashes = Vec::with_capacity(k as usize); for i in 0 .. k { // create a nominator stash. - let (n_stash, n_controller) = create_stash_controller::(MAX_NOMINATIONS as u32 + i, 100, Default::default())?; + let (n_stash, n_controller) = create_stash_controller::( + T::MAX_NOMINATIONS + i, + 100, + Default::default(), + )?; // bake the nominations; we first clone them from the rest of the validators. let mut nominations = rest_of_validators.clone(); @@ -256,9 +264,9 @@ benchmarks! { } } - // Worst case scenario, MAX_NOMINATIONS + // Worst case scenario, T::MAX_NOMINATIONS nominate { - let n in 1 .. MAX_NOMINATIONS as u32; + let n in 1 .. T::MAX_NOMINATIONS; let (stash, controller) = create_stash_controller::(n + 1, 100, Default::default())?; let validators = create_validators::(n, 100)?; whitelist_account!(controller); @@ -467,7 +475,13 @@ benchmarks! { let v in 1 .. 10; let n in 1 .. 100; - create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)?; + create_validators_with_nominators_for_era::( + v, + n, + ::MAX_NOMINATIONS as usize, + false, + None, + )?; let session_index = SessionIndex::one(); }: { let validators = Staking::::new_era(session_index).ok_or("`new_era` failed")?; @@ -478,7 +492,13 @@ benchmarks! { payout_all { let v in 1 .. 10; let n in 1 .. 100; - create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)?; + create_validators_with_nominators_for_era::( + v, + n, + ::MAX_NOMINATIONS as usize, + false, + None, + )?; // Start a new Era let new_validators = Staking::::new_era(SessionIndex::one()).unwrap(); assert!(new_validators.len() == v as usize); @@ -548,7 +568,7 @@ benchmarks! { // total number of slashing spans. Assigned to validators randomly. let s in 1 .. 20; - let validators = create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)? + let validators = create_validators_with_nominators_for_era::(v, n, T::MAX_NOMINATIONS as usize, false, None)? .into_iter() .map(|v| T::Lookup::lookup(v).unwrap()) .collect::>(); @@ -567,7 +587,7 @@ benchmarks! { // number of nominator intention. let n = 500; - let _ = create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)?; + 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); @@ -586,8 +606,13 @@ mod tests { let v = 10; let n = 100; - create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None) - .unwrap(); + create_validators_with_nominators_for_era::( + v, + n, + ::MAX_NOMINATIONS as usize, + false, + None, + ).unwrap(); let count_validators = Validators::::iter().count(); let count_nominators = Nominators::::iter().count(); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index fe1738ca33316..31735f75ebc14 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -284,8 +284,7 @@ use sp_std::{ result, prelude::*, collections::btree_map::BTreeMap, - convert::{TryInto, From}, - mem::size_of, + convert::From, }; use codec::{HasCompact, Encode, Decode}; use frame_support::{ @@ -303,7 +302,7 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Percent, Perbill, PerU16, RuntimeDebug, DispatchError, + Percent, Perbill, RuntimeDebug, DispatchError, curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, @@ -337,22 +336,6 @@ macro_rules! log { }; } -/// Data type used to index nominators in the compact type -pub type NominatorIndex = u32; - -/// Data type used to index validators in the compact type. -pub type ValidatorIndex = u16; - -// Ensure the size of both ValidatorIndex and NominatorIndex. They both need to be well below usize. -static_assertions::const_assert!(size_of::() <= size_of::()); -static_assertions::const_assert!(size_of::() <= size_of::()); -static_assertions::const_assert!(size_of::() <= size_of::()); -static_assertions::const_assert!(size_of::() <= size_of::()); - -/// Maximum number of stakers that can be stored in a snapshot. -pub const MAX_NOMINATIONS: usize = - ::LIMIT; - pub const MAX_UNLOCKING_CHUNKS: usize = 32; /// Counter for the number of eras that have passed. @@ -361,15 +344,6 @@ pub type EraIndex = u32; /// Counter for the number of "reward" points earned by a given validator. pub type RewardPoint = u32; -// Note: Maximum nomination limit is set here -- 16. -sp_npos_elections::generate_solution_type!( - #[compact] - pub struct CompactAssignments::(16) -); - -/// Accuracy used for off-chain election. This better be small. -pub type OffchainAccuracy = PerU16; - /// The balance type of this module. pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -766,6 +740,9 @@ pub trait Config: frame_system::Config + SendTransactionTypes> { 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>; @@ -1213,6 +1190,9 @@ decl_module! { /// 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; @@ -1247,15 +1227,6 @@ decl_module! { T::BondingDuration::get(), ) ); - - use sp_runtime::UpperOf; - // 1. Maximum sum of Vec must fit into `UpperOf`. - assert!( - >>::try_into(MAX_NOMINATIONS) - .unwrap() - .checked_mul(::one().deconstruct().try_into().unwrap()) - .is_some() - ); } /// Take the origin account as a stash and lock up `value` of its balance. `controller` will @@ -1547,7 +1518,7 @@ decl_module! { let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; let stash = &ledger.stash; ensure!(!targets.is_empty(), Error::::EmptyTargets); - ensure!(targets.len() <= MAX_NOMINATIONS, Error::::TooManyTargets); + ensure!(targets.len() <= T::MAX_NOMINATIONS as usize, Error::::TooManyTargets); let old = Nominators::::get(stash).map_or_else(Vec::new, |x| x.targets); @@ -2313,7 +2284,7 @@ impl Module { /// Returns `Err(())` if less than [`MinimumValidatorCount`] validators have been elected, `Ok` /// otherwise. pub fn process_election( - flat_supports: sp_npos_elections::Supports, + flat_supports: frame_election_provider_support::Supports, current_era: EraIndex, ) -> Result, ()> { let exposures = Self::collect_exposures(flat_supports); @@ -2554,6 +2525,7 @@ impl Module { impl frame_election_provider_support::ElectionDataProvider for Module { + const MAXIMUM_VOTES_PER_VOTER: u32 = T::MAX_NOMINATIONS; fn desired_targets() -> data_provider::Result<(u32, Weight)> { Ok((Self::validator_count(), ::DbWeight::get().reads(1))) } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index b4c84059a2106..03f5acfad7286 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -240,6 +240,7 @@ impl onchain::Config for Test { type DataProvider = Staking; } impl Config for Test { + const MAX_NOMINATIONS: u32 = 16; type Currency = Balances; type UnixTime = Timestamp; type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote;