diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a0b1464bae23e..90298297a89c6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,23 +48,39 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux:production" + CI_IMAGE: "paritytech/ci-linux@sha256:9140bc3c843a8b12a3bcf6f5886346536092795bbadfd7f1836362cb28dfcc71" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" RUSTY_CACHIER_COMPRESSION_METHOD: zstd + NEXTEST_FAILURE_OUTPUT: immediate-final + NEXTEST_SUCCESS_OUTPUT: final ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.22" -default: +.shared-default: &shared-default retry: max: 2 when: - runner_system_failure - unknown_failure - api_failure - interruptible: true cache: {} +.default-pipeline-definitions: + default: + <<: *shared-default + interruptible: true + +.crate-publishing-pipeline-definitions: + default: + <<: *shared-default + # The crate-publishing pipeline defaults to `interruptible: false` so that we'll be able to + # reach and run the publishing jobs despite the "Auto-cancel redundant pipelines" CI setting. + # The setting is relevant because the crate-publishing pipeline runs on `master`, thus future + # pipelines on `master` (e.g. created for new commits or other schedules) might unintendedly + # cancel the publishing jobs or its dependencies before we get to actually publish the crates. + interruptible: false + .collect-artifacts: artifacts: name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" @@ -213,9 +229,15 @@ default: # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" +.crate-publishing-pipeline: + rules: + - if: $CI_COMMIT_REF_NAME != "master" + when: never + .scheduled-crate-publishing-pipeline: rules: - - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" + - !reference [.crate-publishing-pipeline, rules] + - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" .crates-publishing-template: stage: test @@ -263,6 +285,22 @@ include: - scripts/ci/gitlab/pipeline/publish.yml # zombienet jobs - scripts/ci/gitlab/pipeline/zombienet.yml + # The crate-publishing pipeline requires a customized `interruptible` configuration. Unfortunately + # `interruptible` can't currently be dynamically set based on variables as per: + # - https://gitlab.com/gitlab-org/gitlab/-/issues/38349 + # - https://gitlab.com/gitlab-org/gitlab/-/issues/194023 + # Thus we work around that limitation by using conditional includes. + # For crate-publishing pipelines: run it with defaults + `interruptible: false`. The WHOLE + # pipeline is made uninterruptible to ensure that test jobs also get a chance to run to + # completion, because the publishing jobs depends on them AS INTENDED: crates should not be + # published before their source code is checked. + - local: scripts/ci/gitlab/crate-publishing-pipeline.yml + rules: + - if: $PIPELINE == "automatic-crate-publishing" + # For normal pipelines: run it with defaults + `interruptible: true` + - local: scripts/ci/gitlab/default-pipeline.yml + rules: + - if: $PIPELINE != "automatic-crate-publishing" #### stage: deploy diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index b7eec8ab60ee0..e950d245dcec9 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -323,7 +323,6 @@ impl NonCanonicalOverlay { &self, level_index: usize, discarded_journals: &mut Vec>, - discarded_blocks: &mut Vec, hash: &BlockHash, ) { if let Some(level) = self.levels.get(level_index) { @@ -335,13 +334,7 @@ impl NonCanonicalOverlay { .clone(); if parent == *hash { discarded_journals.push(overlay.journal_key.clone()); - discarded_blocks.push(overlay.hash.clone()); - self.discard_journals( - level_index + 1, - discarded_journals, - discarded_blocks, - &overlay.hash, - ); + self.discard_journals(level_index + 1, discarded_journals, &overlay.hash); } }); } @@ -393,7 +386,6 @@ impl NonCanonicalOverlay { self.pinned_canonincalized.push(hash.clone()); let mut discarded_journals = Vec::new(); - let mut discarded_blocks = Vec::new(); for (i, overlay) in level.blocks.into_iter().enumerate() { let mut pinned_children = 0; // That's the one we need to canonicalize @@ -411,12 +403,7 @@ impl NonCanonicalOverlay { commit.data.deleted.extend(overlay.deleted.clone()); } else { // Discard this overlay - self.discard_journals( - 0, - &mut discarded_journals, - &mut discarded_blocks, - &overlay.hash, - ); + self.discard_journals(0, &mut discarded_journals, &overlay.hash); pinned_children = discard_descendants( &mut self.levels.as_mut_slices(), &mut self.values, @@ -437,7 +424,6 @@ impl NonCanonicalOverlay { discard_values(&mut self.values, overlay.inserted); } discarded_journals.push(overlay.journal_key.clone()); - discarded_blocks.push(overlay.hash.clone()); } commit.meta.deleted.append(&mut discarded_journals); @@ -548,9 +534,6 @@ impl NonCanonicalOverlay { trace!(target: "state-db-pin", "Discarding unpinned non-canon block: {:?}", hash); discard_values(&mut self.values, inserted); self.parents.remove(&hash); - true - } else { - false } }, Entry::Vacant(_) => break, diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 1853ef75f52be..93e416c3bec4c 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -175,6 +175,7 @@ pub use pallet::*; pub use weights::WeightInfo; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +const LOG_TARGET: &str = "runtime::assets"; /// Trait with callbacks that are executed after successfull asset creation or destruction. pub trait AssetsCallback { diff --git a/frame/assets/src/migration.rs b/frame/assets/src/migration.rs index 89f8d39a9049c..9f8905ceff6c4 100644 --- a/frame/assets/src/migration.rs +++ b/frame/assets/src/migration.rs @@ -75,10 +75,18 @@ pub mod v1 { Some(old_value.migrate_to_v1()) }); current_version.put::>(); - log::info!(target: "runtime::assets", "Upgraded {} pools, storage to version {:?}", translated, current_version); + log::info!( + target: LOG_TARGET, + "Upgraded {} pools, storage to version {:?}", + translated, + current_version + ); T::DbWeight::get().reads_writes(translated + 1, translated + 1) } else { - log::info!(target: "runtime::assets", "Migration did not execute. This probably should be removed"); + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); T::DbWeight::get().reads(1) } } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 57f76b1ff679d..99d77a3e73361 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -193,6 +193,8 @@ pub use weights::WeightInfo; pub use pallet::*; +const LOG_TARGET: &str = "runtime::balances"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] @@ -950,7 +952,7 @@ impl, I: 'static> Pallet { if locks.len() as u32 > T::MaxLocks::get() { log::warn!( - target: "runtime::balances", + target: LOG_TARGET, "Warning: A user has more currency locks than expected. \ A runtime configuration adjustment may be needed." ); @@ -985,7 +987,7 @@ impl, I: 'static> Pallet { // since the funds that are under the lock will themselves be stored in the // account and therefore will need a reference. log::warn!( - target: "runtime::balances", + target: LOG_TARGET, "Warning: Attempt to introduce lock consumer reference, yet no providers. \ This is unexpected but should be safe." ); diff --git a/frame/balances/src/migration.rs b/frame/balances/src/migration.rs index b660ec9fd3235..80d1bcd35ba9e 100644 --- a/frame/balances/src/migration.rs +++ b/frame/balances/src/migration.rs @@ -40,10 +40,13 @@ fn migrate_v0_to_v1, I: 'static>(accounts: &[T::AccountId]) -> Weig // Set storage version to `1`. StorageVersion::new(1).put::>(); - log::info!(target: "runtime::balances", "Storage to version 1"); + log::info!(target: LOG_TARGET, "Storage to version 1"); T::DbWeight::get().reads_writes(2 + accounts.len() as u64, 3) } else { - log::info!(target: "runtime::balances", "Migration did not execute. This probably should be removed"); + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); T::DbWeight::get().reads(1) } } @@ -87,10 +90,13 @@ impl, I: 'static> OnRuntimeUpgrade for ResetInactive { // Set storage version to `0`. StorageVersion::new(0).put::>(); - log::info!(target: "runtime::balances", "Storage to version 0"); + log::info!(target: LOG_TARGET, "Storage to version 0"); T::DbWeight::get().reads_writes(1, 2) } else { - log::info!(target: "runtime::balances", "Migration did not execute. This probably should be removed"); + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); T::DbWeight::get().reads(1) } } diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index c522b71891b3c..0fe05dbb01ac0 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -71,6 +71,8 @@ pub mod weights; pub use pallet::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::collective"; + /// Simple index type for proposal counting. pub type ProposalIndex = u32; @@ -390,7 +392,7 @@ pub mod pallet { ensure_root(origin)?; if new_members.len() > T::MaxMembers::get() as usize { log::error!( - target: "runtime::collective", + target: LOG_TARGET, "New members count ({}) exceeds maximum amount of members expected ({}).", new_members.len(), T::MaxMembers::get(), @@ -400,7 +402,7 @@ pub mod pallet { let old = Members::::get(); if old.len() > old_count as usize { log::warn!( - target: "runtime::collective", + target: LOG_TARGET, "Wrong count used to estimate set_members weight. expected ({}) vs actual ({})", old_count, old.len(), @@ -1040,7 +1042,7 @@ impl, I: 'static> ChangeMembers for Pallet { ) { if new.len() > T::MaxMembers::get() as usize { log::error!( - target: "runtime::collective", + target: LOG_TARGET, "New members count ({}) exceeds maximum amount of members expected ({}).", new.len(), T::MaxMembers::get(), diff --git a/frame/collective/src/migrations/v4.rs b/frame/collective/src/migrations/v4.rs index 483c3f9fa9e69..2756b3fd15a6b 100644 --- a/frame/collective/src/migrations/v4.rs +++ b/frame/collective/src/migrations/v4.rs @@ -17,6 +17,7 @@ use sp_io::hashing::twox_128; +use super::super::LOG_TARGET; use frame_support::{ traits::{ Get, GetStorageVersion, PalletInfoAccess, StorageVersion, @@ -42,7 +43,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::collective", + target: LOG_TARGET, "Running migration to v4 for collective with storage version {:?}", on_chain_storage_version, ); @@ -66,7 +67,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::collective", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -138,7 +139,7 @@ pub fn post_migrate>(old_ fn log_migration(stage: &str, old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::collective", + target: LOG_TARGET, "{}, prefix: '{}' ==> '{}'", stage, old_pallet_name, diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 10041f6aec07c..16263d97da586 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -200,7 +200,7 @@ frame_benchmarking::benchmarks! { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_signed(); + >::phase_transition(Phase::Signed); } verify { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_signed()); @@ -210,7 +210,8 @@ frame_benchmarking::benchmarks! { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_unsigned(true, 1u32.into()) + let now = frame_system::Pallet::::block_number(); + >::phase_transition(Phase::Unsigned((true, now))); } verify { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_unsigned()); @@ -318,7 +319,7 @@ frame_benchmarking::benchmarks! { submit { // the queue is full and the solution is only better than the worse. >::create_snapshot().map_err(<&str>::from)?; - MultiPhase::::on_initialize_open_signed(); + >::phase_transition(Phase::Signed); >::put(1); let mut signed_submissions = SignedSubmissions::::get(); diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 6c4a55800f7e8..6c1b1d163d0fb 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -758,7 +758,7 @@ pub mod pallet { // NOTE: if signed-phase length is zero, second part of the if-condition fails. match Self::create_snapshot() { Ok(_) => { - Self::on_initialize_open_signed(); + Self::phase_transition(Phase::Signed); T::WeightInfo::on_initialize_open_signed() }, Err(why) => { @@ -797,7 +797,7 @@ pub mod pallet { if need_snapshot { match Self::create_snapshot() { Ok(_) => { - Self::on_initialize_open_unsigned(enabled, now); + Self::phase_transition(Phase::Unsigned((enabled, now))); T::WeightInfo::on_initialize_open_unsigned() }, Err(why) => { @@ -806,7 +806,7 @@ pub mod pallet { }, } } else { - Self::on_initialize_open_unsigned(enabled, now); + Self::phase_transition(Phase::Unsigned((enabled, now))); T::WeightInfo::on_initialize_open_unsigned() } }, @@ -931,6 +931,7 @@ pub mod pallet { >::put(ready); Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Unsigned, + origin: None, prev_ejected: ejected_a_solution, }); @@ -983,6 +984,7 @@ pub mod pallet { Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Emergency, + origin: None, prev_ejected: QueuedSolution::::exists(), }); @@ -1060,6 +1062,7 @@ pub mod pallet { signed_submissions.put(); Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Signed, + origin: Some(who), prev_ejected: ejected_a_solution, }); Ok(()) @@ -1102,6 +1105,7 @@ pub mod pallet { Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Fallback, + origin: None, prev_ejected: QueuedSolution::::exists(), }); @@ -1115,11 +1119,16 @@ pub mod pallet { pub enum Event { /// A solution was stored with the given compute. /// - /// If the solution is signed, this means that it hasn't yet been processed. If the - /// solution is unsigned, this means that it has also been processed. - /// - /// The `bool` is `true` when a previous solution was ejected to make room for this one. - SolutionStored { compute: ElectionCompute, prev_ejected: bool }, + /// The `origin` indicates the origin of the solution. If `origin` is `Some(AccountId)`, + /// the stored solution was submited in the signed phase by a miner with the `AccountId`. + /// Otherwise, the solution was stored either during the unsigned phase or by + /// `T::ForceOrigin`. The `bool` is `true` when a previous solution was ejected to make + /// room for this one. + SolutionStored { + compute: ElectionCompute, + origin: Option, + prev_ejected: bool, + }, /// The election has been finalized, with the given computation and score. ElectionFinalized { compute: ElectionCompute, score: ElectionScore }, /// An election failed. @@ -1130,10 +1139,8 @@ pub mod pallet { Rewarded { account: ::AccountId, value: BalanceOf }, /// An account has been slashed for submitting an invalid signed submission. Slashed { account: ::AccountId, value: BalanceOf }, - /// The signed phase of the given round has started. - SignedPhaseStarted { round: u32 }, - /// The unsigned phase of the given round has started. - UnsignedPhaseStarted { round: u32 }, + /// There was a phase transition in a given round. + PhaseTransitioned { from: Phase, to: Phase, round: u32 }, } /// Error of the pallet that can be returned in response to dispatches. @@ -1349,19 +1356,15 @@ impl Pallet { } } - /// Logic for `::on_initialize` when signed phase is being opened. - pub fn on_initialize_open_signed() { - log!(info, "Starting signed phase round {}.", Self::round()); - >::put(Phase::Signed); - Self::deposit_event(Event::SignedPhaseStarted { round: Self::round() }); - } - - /// Logic for `>::on_initialize` when unsigned phase is being opened. - pub fn on_initialize_open_unsigned(enabled: bool, now: T::BlockNumber) { - let round = Self::round(); - log!(info, "Starting unsigned phase round {} enabled {}.", round, enabled); - >::put(Phase::Unsigned((enabled, now))); - Self::deposit_event(Event::UnsignedPhaseStarted { round }); + /// Phase transition helper. + pub(crate) fn phase_transition(to: Phase) { + log!(info, "Starting phase {:?}, round {}.", to, Self::round()); + Self::deposit_event(Event::PhaseTransitioned { + from: >::get(), + to, + round: Self::round(), + }); + >::put(to); } /// Parts of [`create_snapshot`] that happen inside of this pallet. @@ -1571,7 +1574,7 @@ impl Pallet { >::mutate(|r| *r += 1); // Phase is off now. - >::put(Phase::Off); + Self::phase_transition(Phase::Off); // Kill snapshots. Self::kill_snapshot(); @@ -1652,7 +1655,7 @@ impl ElectionProvider for Pallet { }, Err(why) => { log!(error, "Entering emergency mode: {:?}", why); - >::put(Phase::Emergency); + Self::phase_transition(Phase::Emergency); Err(why) }, } @@ -1898,7 +1901,10 @@ mod tests { roll_to_signed(); assert_eq!(MultiPhase::current_phase(), Phase::Signed); - assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted { round: 1 }]); + assert_eq!( + multi_phase_events(), + vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] + ); assert!(MultiPhase::snapshot().is_some()); assert_eq!(MultiPhase::round(), 1); @@ -1912,8 +1918,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 } + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, ], ); assert!(MultiPhase::snapshot().is_some()); @@ -1949,8 +1959,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -1959,8 +1973,17 @@ mod tests { sum_stake_squared: 0 } }, - Event::SignedPhaseStarted { round: 2 }, - Event::UnsignedPhaseStarted { round: 2 } + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 2 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 55)), + round: 2 + }, ] ); }) @@ -1990,7 +2013,11 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { + from: Phase::Off, + to: Phase::Unsigned((true, 20)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -1998,7 +2025,12 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 20)), + to: Phase::Off, + round: 2 + }, ] ); }); @@ -2028,7 +2060,7 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -2036,7 +2068,8 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 }, ] ) }); @@ -2064,10 +2097,17 @@ mod tests { assert_eq!( multi_phase_events(), - vec![Event::ElectionFinalized { - compute: ElectionCompute::Fallback, - score: ElectionScore { minimal_stake: 0, sum_stake: 0, sum_stake_squared: 0 } - }] + vec![ + Event::ElectionFinalized { + compute: ElectionCompute::Fallback, + score: ElectionScore { + minimal_stake: 0, + sum_stake: 0, + sum_stake_squared: 0 + } + }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Off, round: 2 }, + ] ); }); } @@ -2079,7 +2119,10 @@ mod tests { // Signed phase started at block 15 and will end at 25. roll_to_signed(); - assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted { round: 1 }]); + assert_eq!( + multi_phase_events(), + vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] + ); assert_eq!(MultiPhase::current_phase(), Phase::Signed); assert_eq!(MultiPhase::round(), 1); @@ -2090,11 +2133,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: Default::default() - } + }, + Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 }, ], ); // All storage items must be cleared. @@ -2114,7 +2158,10 @@ mod tests { // signed phase started at block 15 and will end at 25. roll_to_signed(); - assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted { round: 1 }]); + assert_eq!( + multi_phase_events(), + vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] + ); assert_eq!(MultiPhase::current_phase(), Phase::Signed); assert_eq!(MultiPhase::round(), 1); @@ -2144,12 +2191,32 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Slashed { account: 99, value: 5 }, Event::Slashed { account: 99, value: 5 }, Event::Slashed { account: 99, value: 5 }, @@ -2162,7 +2229,8 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 }, ] ); }) @@ -2186,10 +2254,18 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Signed, score: ElectionScore { @@ -2197,7 +2273,12 @@ mod tests { sum_stake: 100, sum_stake_squared: 5200 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, ], ); }) @@ -2230,10 +2311,15 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::SolutionStored { compute: ElectionCompute::Unsigned, + origin: None, prev_ejected: false }, Event::ElectionFinalized { @@ -2243,7 +2329,12 @@ mod tests { sum_stake: 100, sum_stake_squared: 5200 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, ], ); }) @@ -2270,8 +2361,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -2279,7 +2374,12 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, ] ); }); @@ -2299,9 +2399,18 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, - Event::ElectionFailed + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, + Event::ElectionFailed, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Emergency, + round: 1 + }, ] ); }) @@ -2339,17 +2448,28 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFailed, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Emergency, + round: 1 + }, Event::SolutionStored { compute: ElectionCompute::Fallback, + origin: None, prev_ejected: false }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: Default::default() - } + }, + Event::PhaseTransitioned { from: Phase::Emergency, to: Phase::Off, round: 2 }, ] ); }) @@ -2375,10 +2495,17 @@ mod tests { assert_eq!( multi_phase_events(), - vec![Event::ElectionFinalized { - compute: ElectionCompute::Fallback, - score: ElectionScore { minimal_stake: 0, sum_stake: 0, sum_stake_squared: 0 } - }] + vec![ + Event::ElectionFinalized { + compute: ElectionCompute::Fallback, + score: ElectionScore { + minimal_stake: 0, + sum_stake: 0, + sum_stake_squared: 0 + } + }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Off, round: 2 }, + ] ); }); } @@ -2404,7 +2531,13 @@ mod tests { assert_eq!(err, ElectionError::Fallback("NoFallback.")); assert_eq!(MultiPhase::current_phase(), Phase::Emergency); - assert_eq!(multi_phase_events(), vec![Event::ElectionFailed]); + assert_eq!( + multi_phase_events(), + vec![ + Event::ElectionFailed, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Emergency, round: 1 } + ] + ); }); } diff --git a/frame/election-provider-multi-phase/src/signed.rs b/frame/election-provider-multi-phase/src/signed.rs index 12d39e83b6c09..895f3670a7f0d 100644 --- a/frame/election-provider-multi-phase/src/signed.rs +++ b/frame/election-provider-multi-phase/src/signed.rs @@ -620,8 +620,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false } + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + } ] ); }) @@ -645,8 +649,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 } ] ); @@ -676,8 +684,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Slashed { account: 99, value: 5 } ] ); @@ -713,9 +725,17 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(999), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 } ] ); @@ -788,12 +808,32 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(100), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(101), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(102), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(103), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 }, Event::ElectionFinalized { compute: ElectionCompute::Signed, @@ -856,13 +896,15 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, Event::SolutionStored { compute: ElectionCompute::Signed, + origin: Some(99), prev_ejected: false }, Event::SolutionStored { compute: ElectionCompute::Signed, + origin: Some(99), prev_ejected: true } ] @@ -1112,12 +1154,28 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(100), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(101), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(102), + prev_ejected: false + }, Event::Rewarded { account: 100, value: 7 }, - Event::UnsignedPhaseStarted { round: 1 } + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, ] ); }) @@ -1170,10 +1228,22 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(999), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(9999), + prev_ejected: false + }, Event::Slashed { account: 999, value: 5 }, Event::Rewarded { account: 99, value: 7 } ] @@ -1304,8 +1374,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 } ] ); diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 7340605dfe621..cdc71f7bf5bf0 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -1055,7 +1055,7 @@ mod tests { Runtime, RuntimeCall, RuntimeOrigin, System, TestNposSolution, TrimHelpers, UnsignedPhase, }, - CurrentPhase, Event, InvalidTransaction, Phase, QueuedSolution, TransactionSource, + Event, InvalidTransaction, Phase, QueuedSolution, TransactionSource, TransactionValidityError, }; use codec::Decode; @@ -1128,7 +1128,7 @@ mod tests { assert!(::pre_dispatch(&call).is_ok()); // unsigned -- but not enabled. - >::put(Phase::Unsigned((false, 25))); + MultiPhase::phase_transition(Phase::Unsigned((false, 25))); assert!(MultiPhase::current_phase().is_unsigned()); assert!(matches!( ::validate_unsigned( @@ -1321,10 +1321,15 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::SolutionStored { compute: ElectionCompute::Unsigned, + origin: None, prev_ejected: false } ] @@ -1661,7 +1666,7 @@ mod tests { let current_block = block_plus(offchain_repeat * 2 + 2); // force the unsigned phase to start on the current block. - CurrentPhase::::set(Phase::Unsigned((true, current_block))); + MultiPhase::phase_transition(Phase::Unsigned((true, current_block))); // clear the cache and create a solution since we are on the first block of the unsigned // phase. @@ -1673,8 +1678,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -1682,7 +1691,12 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Unsigned((true, 37)), + round: 1 + }, ] ); }) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 1cfdc25fd9b47..1a020adb28632 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -122,6 +122,8 @@ pub use weights::WeightInfo; /// All migrations. pub mod migrations; +const LOG_TARGET: &str = "runtime::elections-phragmen"; + /// The maximum votes allowed per voter. pub const MAXIMUM_VOTE: usize = 16; @@ -789,10 +791,7 @@ impl Pallet { } else { // overlap. This can never happen. If so, it seems like our intended replacement // is already a member, so not much more to do. - log::error!( - target: "runtime::elections-phragmen", - "A member seems to also be a runner-up.", - ); + log::error!(target: LOG_TARGET, "A member seems to also be a runner-up."); } next_best }); @@ -939,7 +938,7 @@ impl Pallet { Ok(_) => (), Err(_) => { log::error!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Failed to run election. Number of voters exceeded", ); Self::deposit_event(Event::ElectionError); @@ -1103,11 +1102,7 @@ impl Pallet { >::mutate(|v| *v += 1); }) .map_err(|e| { - log::error!( - target: "runtime::elections-phragmen", - "Failed to run election [{:?}].", - e, - ); + log::error!(target: LOG_TARGET, "Failed to run election [{:?}].", e,); Self::deposit_event(Event::ElectionError); }); diff --git a/frame/elections-phragmen/src/migrations/v3.rs b/frame/elections-phragmen/src/migrations/v3.rs index 9ec9c6e7eea6c..e48cd6c1a8056 100644 --- a/frame/elections-phragmen/src/migrations/v3.rs +++ b/frame/elections-phragmen/src/migrations/v3.rs @@ -17,6 +17,7 @@ //! Migrations to version [`3.0.0`], as denoted by the changelog. +use super::super::LOG_TARGET; use crate::{Config, Pallet}; use codec::{Decode, Encode, FullCodec}; use frame_support::{ @@ -88,7 +89,7 @@ pub fn apply( ) -> Weight { let storage_version = StorageVersion::get::>(); log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Running migration for elections-phragmen with storage version {:?}", storage_version, ); @@ -104,7 +105,7 @@ pub fn apply( Weight::MAX } else { log::warn!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Attempted to apply migration to V3 but failed because storage version is {:?}", storage_version, ); @@ -118,22 +119,14 @@ pub fn migrate_voters_to_recorded_deposit(old_deposit: V:: Some(Voter { votes, stake, deposit: old_deposit }) }); - log::info!( - target: "runtime::elections-phragmen", - "migrated {} voter accounts.", - >::iter().count(), - ); + log::info!(target: LOG_TARGET, "migrated {} voter accounts.", >::iter().count()); } /// Migrate all candidates to recorded deposit. pub fn migrate_candidates_to_recorded_deposit(old_deposit: V::Balance) { let _ = >::translate::, _>(|maybe_old_candidates| { maybe_old_candidates.map(|old_candidates| { - log::info!( - target: "runtime::elections-phragmen", - "migrated {} candidate accounts.", - old_candidates.len(), - ); + log::info!(target: LOG_TARGET, "migrated {} candidate accounts.", old_candidates.len()); old_candidates.into_iter().map(|c| (c, old_deposit)).collect::>() }) }); @@ -143,11 +136,7 @@ pub fn migrate_candidates_to_recorded_deposit(old_deposit: pub fn migrate_members_to_recorded_deposit(old_deposit: V::Balance) { let _ = >::translate::, _>(|maybe_old_members| { maybe_old_members.map(|old_members| { - log::info!( - target: "runtime::elections-phragmen", - "migrated {} member accounts.", - old_members.len(), - ); + log::info!(target: LOG_TARGET, "migrated {} member accounts.", old_members.len()); old_members .into_iter() .map(|(who, stake)| SeatHolder { who, stake, deposit: old_deposit }) @@ -162,7 +151,7 @@ pub fn migrate_runners_up_to_recorded_deposit(old_deposit: |maybe_old_runners_up| { maybe_old_runners_up.map(|old_runners_up| { log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "migrated {} runner-up accounts.", old_runners_up.len(), ); diff --git a/frame/elections-phragmen/src/migrations/v4.rs b/frame/elections-phragmen/src/migrations/v4.rs index 76ef630706c50..535a9fb8201d7 100644 --- a/frame/elections-phragmen/src/migrations/v4.rs +++ b/frame/elections-phragmen/src/migrations/v4.rs @@ -17,6 +17,7 @@ //! Migrations to version [`4.0.0`], as denoted by the changelog. +use super::super::LOG_TARGET; use frame_support::{ traits::{Get, StorageVersion}, weights::Weight, @@ -35,14 +36,14 @@ pub const OLD_PREFIX: &[u8] = b"PhragmenElection"; pub fn migrate>(new_pallet_name: N) -> Weight { if new_pallet_name.as_ref().as_bytes() == OLD_PREFIX { log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "New pallet name is equal to the old prefix. No migration needs to be done.", ); return Weight::zero() } let storage_version = StorageVersion::get::>(); log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Running migration to v4 for elections-phragmen with storage version {:?}", storage_version, ); @@ -59,7 +60,7 @@ pub fn migrate>(new_pallet_name: N) -> Weight { ::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", storage_version, ); diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index f2faeebc13478..c0bffc4427a11 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -147,6 +147,8 @@ pub mod pallet { /// The map of all accounts wishing to be unstaked. /// /// Keeps track of `AccountId` wishing to unstake and it's corresponding deposit. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] pub type Queue = CountedStorageMap<_, Twox64Concat, T::AccountId, BalanceOf>; diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 8531af120ec88..934abb996df80 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -36,6 +36,8 @@ pub mod weights; pub use pallet::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::membership"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] diff --git a/frame/membership/src/migrations/v4.rs b/frame/membership/src/migrations/v4.rs index 5b8735aa2bac9..cbc58cd6ae917 100644 --- a/frame/membership/src/migrations/v4.rs +++ b/frame/membership/src/migrations/v4.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::super::LOG_TARGET; use sp_io::hashing::twox_128; use frame_support::{ @@ -43,7 +44,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::membership", + target: LOG_TARGET, "Running migration to v4 for membership with storage version {:?}", on_chain_storage_version, ); @@ -67,7 +68,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::membership", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -139,7 +140,7 @@ pub fn post_migrate>(old_pallet_name: N, new fn log_migration(stage: &str, old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::membership", + target: LOG_TARGET, "{}, prefix: '{}' ==> '{}'", stage, old_pallet_name, diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 9b063539152b7..7b0062b9c9231 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -28,11 +28,15 @@ use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ - BalanceOf, BondExtra, BondedPoolInner, BondedPools, ConfigOp, MaxPoolMembers, - MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, - PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, + BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionChangeRate, ConfigOp, + GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, + MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, + SubPoolsStorage, +}; +use sp_runtime::{ + traits::{Bounded, StaticLookup, Zero}, + Perbill, }; -use sp_runtime::traits::{Bounded, StaticLookup, Zero}; use sp_staking::{EraIndex, StakingInterface}; // `frame_benchmarking::benchmarks!` macro needs this use pallet_nomination_pools::Call; @@ -67,6 +71,7 @@ fn create_funded_user_with_balance( fn create_pool_account( n: u32, balance: BalanceOf, + commission: Option, ) -> (T::AccountId, T::AccountId) { let ed = CurrencyOf::::minimum_balance(); let pool_creator: T::AccountId = @@ -82,6 +87,16 @@ fn create_pool_account( ) .unwrap(); + if let Some(c) = commission { + let pool_id = LastPoolId::::get(); + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator.clone()).into(), + pool_id, + Some((c, pool_creator.clone())), + ) + .expect("pool just created, commission can be set by root; qed"); + } + let pool_account = pallet_nomination_pools::BondedPools::::iter() .find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator) .map(|(pool_id, _)| Pools::::create_bonded_account(pool_id)) @@ -132,14 +147,18 @@ impl ListScenario { sp_std::mem::forget(i); // Create accounts with the origin weight - let (pool_creator1, pool_origin1) = create_pool_account::(USER_SEED + 1, origin_weight); + let (pool_creator1, pool_origin1) = + create_pool_account::(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate( &pool_origin1, // NOTE: these don't really need to be validators. vec![account("random_validator", 0, USER_SEED)], )?; - let (_, pool_origin2) = create_pool_account::(USER_SEED + 2, origin_weight); + let (_, pool_origin2) = + create_pool_account::(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate( &pool_origin2, vec![account("random_validator", 0, USER_SEED)].clone(), @@ -155,7 +174,9 @@ impl ListScenario { dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?; // Create an account with the worst case destination weight - let (_, pool_dest1) = create_pool_account::(USER_SEED + 3, dest_weight); + let (_, pool_dest1) = + create_pool_account::(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?; let weight_of = pallet_staking::Pallet::::weight_of_fn(); @@ -262,16 +283,17 @@ frame_benchmarking::benchmarks! { }: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::Rewards) verify { + // commission of 50% deducted here. assert!( T::Staking::active_stake(&scenario.origin1).unwrap() >= - scenario.dest_weight + scenario.dest_weight / 2u32.into() ); } claim_payout { let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); let ed = CurrencyOf::::minimum_balance(); - let (depositor, pool_account) = create_pool_account::(0, origin_weight); + let (depositor, pool_account) = create_pool_account::(0, origin_weight, Some(Perbill::from_percent(50))); let reward_account = Pools::::create_reward_account(1); // Send funds to the reward account of the pool @@ -331,7 +353,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Add a new member let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -373,7 +395,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Add a new member let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -419,7 +441,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); let depositor_lookup = T::Lookup::unlookup(depositor.clone()); // We set the pool to the destroying state so the depositor can leave @@ -509,15 +531,16 @@ frame_benchmarking::benchmarks! { assert_eq!( new_pool, BondedPoolInner { - points: min_create_bond, - state: PoolState::Open, + commission: Commission::default(), member_counter: 1, + points: min_create_bond, roles: PoolRoles { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), state_toggler: Some(depositor.clone()), }, + state: PoolState::Open, } ); assert_eq!( @@ -531,7 +554,7 @@ frame_benchmarking::benchmarks! { // Create a pool let min_create_bond = Pools::::depositor_min_bond() * 2u32.into(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Create some accounts to nominate. For the sake of benchmarking they don't need to be // actual validators @@ -548,15 +571,16 @@ frame_benchmarking::benchmarks! { assert_eq!( new_pool, BondedPoolInner { - points: min_create_bond, - state: PoolState::Open, + commission: Commission::default(), member_counter: 1, + points: min_create_bond, roles: PoolRoles { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), state_toggler: Some(depositor.clone()), - } + }, + state: PoolState::Open, } ); assert_eq!( @@ -568,7 +592,7 @@ frame_benchmarking::benchmarks! { set_state { // Create a pool let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); BondedPools::::mutate(&1, |maybe_pool| { // Force the pool into an invalid state maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into()); @@ -585,7 +609,7 @@ frame_benchmarking::benchmarks! { let n in 1 .. ::MaxMetadataLen::get(); // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // Create metadata of the max possible size let metadata: Vec = (0..n).map(|_| 42).collect(); @@ -603,18 +627,20 @@ frame_benchmarking::benchmarks! { ConfigOp::Set(BalanceOf::::max_value()), ConfigOp::Set(u32::MAX), ConfigOp::Set(u32::MAX), - ConfigOp::Set(u32::MAX) + ConfigOp::Set(u32::MAX), + ConfigOp::Set(Perbill::max_value()) ) verify { assert_eq!(MinJoinBond::::get(), BalanceOf::::max_value()); assert_eq!(MinCreateBond::::get(), BalanceOf::::max_value()); assert_eq!(MaxPools::::get(), Some(u32::MAX)); assert_eq!(MaxPoolMembers::::get(), Some(u32::MAX)); assert_eq!(MaxPoolMembersPerPool::::get(), Some(u32::MAX)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::max_value())); } update_roles { let first_id = pallet_nomination_pools::LastPoolId::::get() + 1; - let (root, _) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (root, _) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED); }:_( RuntimeOrigin::Signed(root.clone()), @@ -636,7 +662,7 @@ frame_benchmarking::benchmarks! { chill { // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // Nominate with the pool. let validators: Vec<_> = (0..T::MaxNominations::get()) @@ -652,6 +678,64 @@ frame_benchmarking::benchmarks! { assert!(T::Staking::nominations(Pools::::create_bonded_account(1)).is_none()); } + set_commission { + // Create a pool - do not set a commission yet. + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); + // set a max commission + Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap(); + // set a change rate + Pools::::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate { + max_increase: Perbill::from_percent(20), + min_delay: 0u32.into(), + }).unwrap(); + + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone()))) + verify { + assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { + current: Some((Perbill::from_percent(20), depositor)), + max: Some(Perbill::from_percent(50)), + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(20), + min_delay: 0u32.into() + }), + throttle_from: Some(1u32.into()), + }); + } + + set_commission_max { + // Create a pool, setting a commission that will update when max commission is set. + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50))); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50)) + verify { + assert_eq!( + BondedPools::::get(1).unwrap().commission, Commission { + current: Some((Perbill::from_percent(50), depositor)), + max: Some(Perbill::from_percent(50)), + change_rate: None, + throttle_from: Some(0u32.into()), + }); + } + + set_commission_change_rate { + // Create a pool + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate { + max_increase: Perbill::from_percent(50), + min_delay: 1000u32.into(), + }) + verify { + assert_eq!( + BondedPools::::get(1).unwrap().commission, Commission { + current: None, + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(50), + min_delay: 1000u32.into(), + }), + throttle_from: Some(1_u32.into()), + }); + } + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext(), diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 06a66838594c7..db097004ef73b 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -20,7 +20,7 @@ use frame_election_provider_support::VoteWeight; use frame_support::{pallet_prelude::*, parameter_types, traits::ConstU64, PalletId}; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128, + FixedU128, Perbill, }; type AccountId = u128; @@ -195,6 +195,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(3), max_members: Some(3 * 3), + global_max_commission: Some(Perbill::from_percent(500)), } .assimilate_storage(&mut storage); sp_io::TestExternalities::from(storage) diff --git a/frame/nomination-pools/fuzzer/src/call.rs b/frame/nomination-pools/fuzzer/src/call.rs index b07903609e8ab..121271bb67f6e 100644 --- a/frame/nomination-pools/fuzzer/src/call.rs +++ b/frame/nomination-pools/fuzzer/src/call.rs @@ -33,11 +33,11 @@ use pallet_nomination_pools::{ mock::*, pallet as pools, pallet::{BondedPools, Call as PoolsCall, Event as PoolsEvents, PoolMembers}, - BondExtra, BondedPool, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, - MinCreateBond, MinJoinBond, PoolId, + BondExtra, BondedPool, GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, + MaxPools, MinCreateBond, MinJoinBond, PoolId, }; use rand::{seq::SliceRandom, Rng}; -use sp_runtime::{assert_eq_error_rate, Perquintill}; +use sp_runtime::{assert_eq_error_rate, Perbill, Perquintill}; const ERA: BlockNumber = 1000; const MAX_ED_MULTIPLE: Balance = 10_000; @@ -224,6 +224,7 @@ fn main() { MaxPoolMembers::::set(Some(10_000)); MaxPoolMembersPerPool::::set(Some(1000)); MaxPools::::set(Some(1_000)); + GlobalMaxCommission::::set(Some(Perbill::from_percent(90))); MinCreateBond::::set(10 * ExistentialDeposit::get()); MinJoinBond::::set(5 * ExistentialDeposit::get()); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 99bfc8b8c36a5..a09e55e5d543b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -334,9 +334,10 @@ use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::{ traits::{ - AccountIdConversion, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, Zero, + AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, + Zero, }, - FixedPointNumber, + FixedPointNumber, Perbill, }; use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; @@ -582,25 +583,234 @@ pub struct PoolRoles { pub state_toggler: Option, } +/// Pool commission. +/// +/// The pool `root` can set commission configuration after pool creation. By default, all commission +/// values are `None`. Pool `root` can also set `max` and `change_rate` configurations before +/// setting an initial `current` commission. +/// +/// `current` is a tuple of the commission percentage and payee of commission. `throttle_from` +/// keeps track of which block `current` was last updated. A `max` commission value can only be +/// decreased after the initial value is set, to prevent commission from repeatedly increasing. +/// +/// An optional commission `change_rate` allows the pool to set strict limits to how much commission +/// can change in each update, and how often updates can take place. +#[derive( + Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone, +)] +#[codec(mel_bound(T: Config))] +#[scale_info(skip_type_params(T))] +pub struct Commission { + /// Optional commission rate of the pool along with the account commission is paid to. + pub current: Option<(Perbill, T::AccountId)>, + /// Optional maximum commission that can be set by the pool `root`. Once set, this value can + /// only be updated to a decreased value. + pub max: Option, + /// Optional configuration around how often commission can be updated, and when the last + /// commission update took place. + pub change_rate: Option>, + /// The block throttling should be checked from. This value will be updated on all commission + /// updates and when setting an initial `change_rate`. + pub throttle_from: Option, +} + +impl Commission { + /// Returns true if the current commission updating to `to` would exhaust the change rate + /// limits. + /// + /// A commission update will be throttled (disallowed) if: + /// 1. not enough blocks have passed since the `throttle_from` block, if exists, or + /// 2. the new commission is greater than the maximum allowed increase. + fn throttling(&self, to: &Perbill) -> bool { + if let Some(t) = self.change_rate.as_ref() { + let commission_as_percent = + self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()); + + // do not throttle if `to` is the same or a decrease in commission. + if *to <= commission_as_percent { + return false + } + // Test for `max_increase` throttling. + // + // Throttled if the attempted increase in commission is greater than `max_increase`. + if (*to).saturating_sub(commission_as_percent) > t.max_increase { + return true + } + + // Test for `min_delay` throttling. + // + // Note: matching `None` is defensive only. `throttle_from` should always exist where + // `change_rate` has already been set, so this scenario should never happen. + return self.throttle_from.map_or_else( + || { + defensive!("throttle_from should exist if change_rate is set"); + false + }, + |f| { + // if `min_delay` is zero (no delay), not throttling. + if t.min_delay == Zero::zero() { + return false + } else { + // throttling if blocks passed is less than `min_delay`. + let blocks_surpassed = + >::block_number().saturating_sub(f); + return blocks_surpassed < t.min_delay + } + }, + ) + } + false + } + + /// Set the pool's commission. + /// + /// Update commission based on `current`. If a `None` is supplied, allow the commission to be + /// removed without any change rate restrictions. If `change_rate` is present, update + /// `throttle_from` to the current block. If the supplied commission is zero, `None` will be + /// inserted and `payee` will be ignored. + fn try_update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult { + self.current = match current { + None => None, + Some((commission, payee)) => { + ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); + ensure!( + self.max.map_or(true, |m| commission <= &m), + Error::::CommissionExceedsMaximum + ); + if commission.is_zero() { + None + } else { + Some((*commission, payee.clone())) + } + }, + }; + let _ = self.register_update(); + Ok(()) + } + + /// Set the pool's maximum commission. + /// + /// The pool's maximum commission can initially be set to any value, and only smaller values + /// thereafter. If larger values are attempted, this function will return a dispatch error. + /// + /// If `current.0` is larger than the updated max commission value, `current.0` will also be + /// updated to the new maximum. This will also register a `throttle_from` update. + fn try_update_max(&mut self, new_max: Perbill) -> DispatchResult { + if let Some(old) = self.max.as_mut() { + if new_max > *old { + return Err(Error::::MaxCommissionRestricted.into()) + } + *old = new_max; + } else { + self.max = Some(new_max) + }; + let updated_current = self + .current + .as_mut() + .map(|(c, _)| { + let u = *c > new_max; + *c = (*c).min(new_max); + u + }) + .unwrap_or(false); + + if updated_current { + self.register_update(); + } + Ok(()) + } + + /// Set the pool's commission `change_rate`. + /// + /// Once a change rate configuration has been set, only more restrictive values can be set + /// thereafter. These restrictions translate to increased `min_delay` values and decreased + /// `max_increase` values. + /// + /// Update `throttle_from` to the current block upon setting change rate for the first time, so + /// throttling can be checked from this block. + fn try_update_change_rate( + &mut self, + change_rate: CommissionChangeRate, + ) -> DispatchResult { + ensure!(!&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); + + if self.change_rate.is_none() { + self.throttle_from = Some(>::block_number()); + } + self.change_rate = Some(change_rate); + Ok(()) + } + + /// Gets the current commission (if any) and payee to be paid. + /// + /// `None` is returned if a commission has not been set. Commission is bounded to + /// `GlobalMaxCommission`. + fn maybe_commission_and_payee( + &self, + pending_rewards: &BalanceOf, + ) -> Option<(BalanceOf, T::AccountId)> { + self.current.as_ref().map(|(commission, payee)| { + ( + *commission.min(&GlobalMaxCommission::::get().unwrap_or(Bounded::max_value())) * + *pending_rewards, + payee.clone(), + ) + }) + } + + /// Updates a commission's `throttle_from` field to the current block. + fn register_update(&mut self) { + self.throttle_from = Some(>::block_number()); + } + + /// Checks whether a change rate is less restrictive than the current change rate, if any. + /// + /// No change rate will always be less restrictive than some change rate, so where no + /// `change_rate` is currently set, `false` is returned. + fn less_restrictive(&self, new: &CommissionChangeRate) -> bool { + self.change_rate + .as_ref() + .map(|c| new.max_increase > c.max_increase || new.min_delay < c.min_delay) + .unwrap_or(false) + } +} + +/// Pool commission change rate preferences. +/// +/// The pool root is able to set a commission change rate for their pool. A commission change rate +/// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of +/// blocks that must elapse before commission updates are allowed again. +/// +/// Commission change rates are not applied to decreases in commission. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] +pub struct CommissionChangeRate { + /// The maximum amount the commission can be updated by per `min_delay` period. + pub max_increase: Perbill, + /// How often an update can take place. + pub min_delay: BlockNumber, +} + /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct BondedPoolInner { - /// Total points of all the members in the pool who are actively bonded. - pub points: BalanceOf, - /// The current state of the pool. - pub state: PoolState, + /// The commission rate of the pool. + pub commission: Commission, /// Count of members that belong to the pool. pub member_counter: u32, + /// Total points of all the members in the pool who are actively bonded. + pub points: BalanceOf, /// See [`PoolRoles`]. pub roles: PoolRoles, + /// The current state of the pool. + pub state: PoolState, } /// A wrapper for bonded pools, with utility functions. /// -/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account + id of the pool, -/// for easier access. +/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account +/// + id of the pool, for easier access. #[derive(RuntimeDebugNoBound)] #[cfg_attr(feature = "std", derive(Clone, PartialEq))] pub struct BondedPool { @@ -629,10 +839,11 @@ impl BondedPool { Self { id, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: Zero::zero(), + points: Zero::zero(), roles, state: PoolState::Open, - points: Zero::zero(), - member_counter: Zero::zero(), }, } } @@ -762,6 +973,10 @@ impl BondedPool { self.is_root(who) || self.is_state_toggler(who) } + fn can_set_commission(&self, who: &T::AccountId) -> bool { + self.is_root(who) + } + fn is_destroying(&self) -> bool { matches!(self.state, PoolState::Destroying) } @@ -1171,9 +1386,10 @@ pub mod pallet { use super::*; use frame_support::traits::StorageVersion; use frame_system::{ensure_signed, pallet_prelude::*}; + use sp_runtime::Perbill; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] @@ -1276,7 +1492,15 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; + /// The maximum commission that can be charged by a pool. Used on commission payouts to bound + /// pool commissions that are > GlobalMaxCommission, necessary if a future `GlobalMaxCommission` + /// is lower than some current pool commissions. + #[pallet::storage] + pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; + /// Active members. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] pub type PoolMembers = CountedStorageMap<_, Twox64Concat, T::AccountId, PoolMember>; @@ -1287,13 +1511,13 @@ pub mod pallet { pub type BondedPools = CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner>; - /// Reward pools. This is where there rewards for each pool accumulate. When a members payout - /// is claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. + /// Reward pools. This is where there rewards for each pool accumulate. When a members payout is + /// claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. #[pallet::storage] pub type RewardPools = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool>; - /// Groups of unbonding pools. Each group of unbonding pools belongs to a bonded pool, - /// hence the name sub-pools. Keyed by the bonded pools account. + /// Groups of unbonding pools. Each group of unbonding pools belongs to a + /// bonded pool, hence the name sub-pools. Keyed by the bonded pools account. #[pallet::storage] pub type SubPoolsStorage = CountedStorageMap<_, Twox64Concat, PoolId, SubPools>; @@ -1321,6 +1545,7 @@ pub mod pallet { pub max_pools: Option, pub max_members_per_pool: Option, pub max_members: Option, + pub global_max_commission: Option, } #[cfg(feature = "std")] @@ -1332,6 +1557,7 @@ pub mod pallet { max_pools: Some(16), max_members_per_pool: Some(32), max_members: Some(16 * 32), + global_max_commission: None, } } } @@ -1350,6 +1576,9 @@ pub mod pallet { if let Some(max_members) = self.max_members { MaxPoolMembers::::put(max_members); } + if let Some(global_max_commission) = self.global_max_commission { + GlobalMaxCommission::::put(global_max_commission); + } } } @@ -1362,7 +1591,12 @@ pub mod pallet { /// A member has became bonded in a pool. Bonded { member: T::AccountId, pool_id: PoolId, bonded: BalanceOf, joined: bool }, /// A payout has been made to a member. - PaidOut { member: T::AccountId, pool_id: PoolId, payout: BalanceOf }, + PaidOut { + member: T::AccountId, + pool_id: PoolId, + payout: BalanceOf, + commission: BalanceOf, + }, /// A member has unbonded from their pool. /// /// - `balance` is the corresponding balance of the number of points that has been @@ -1412,6 +1646,15 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, + /// A pool's commission setting has been changed. + PoolCommissionUpdated { pool_id: PoolId, current: Option<(Perbill, T::AccountId)> }, + /// A pool's maximum commission setting has been changed. + PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill }, + /// A pool's commission `change_rate` has been changed. + PoolCommissionChangeRateUpdated { + pool_id: PoolId, + change_rate: CommissionChangeRate, + }, } #[pallet::error] @@ -1467,6 +1710,18 @@ pub mod pallet { Defensive(DefensiveError), /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, + /// No commission has been set. + NoCommissionSet, + /// No account has been set to receive commission. + NoCommissionPayeeSet, + /// The pool's max commission cannot be set higher than the existing value. + MaxCommissionRestricted, + /// The supplied commission exceeds the max allowed commission. + CommissionExceedsMaximum, + /// Not enough blocks have surpassed since the last commission update. + CommissionChangeThrottled, + /// The submitted changes to commission change rate are not allowed. + CommissionChangeRateNotAllowed, /// Pool id currently in use. PoolIdInUse, /// Pool id provided is not correct/usable. @@ -1602,7 +1857,7 @@ pub mod pallet { } /// A bonded member can use this to claim their payout based on the rewards that the pool - /// has accumulated since their last claimed payout (OR since joining if this is there first + /// has accumulated since their last claimed payout (OR since joining if this is their first /// time claiming rewards). The payout will be transferred to the member's account. /// /// The member will earn rewards pro rata based on the members stake vs the sum of the @@ -2017,6 +2272,7 @@ pub mod pallet { /// * `max_pools` - Set [`MaxPools`]. /// * `max_members` - Set [`MaxPoolMembers`]. /// * `max_members_per_pool` - Set [`MaxPoolMembersPerPool`]. + /// * `global_max_commission` - Set [`GlobalMaxCommission`]. #[pallet::call_index(11)] #[pallet::weight(T::WeightInfo::set_configs())] pub fn set_configs( @@ -2026,6 +2282,7 @@ pub mod pallet { max_pools: ConfigOp, max_members: ConfigOp, max_members_per_pool: ConfigOp, + global_max_commission: ConfigOp, ) -> DispatchResult { ensure_root(origin)?; @@ -2044,6 +2301,7 @@ pub mod pallet { config_op_exp!(MaxPools::, max_pools); config_op_exp!(MaxPoolMembers::, max_members); config_op_exp!(MaxPoolMembersPerPool::, max_members_per_pool); + config_op_exp!(GlobalMaxCommission::, global_max_commission); Ok(()) } @@ -2115,6 +2373,76 @@ pub mod pallet { ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); T::Staking::chill(&bonded_pool.bonded_account()) } + + /// Set the commission of a pool. + /// + /// The dispatch origin of this call must be signed by the `root` role of the pool. Both a + /// commission percentage and a commission payee must be provided in the `current` tuple. + /// Where a `current` of `None` is provided, any current commission will be removed. + #[pallet::call_index(14)] + #[pallet::weight(T::WeightInfo::set_commission())] + pub fn set_commission( + origin: OriginFor, + pool_id: PoolId, + new_commission: Option<(Perbill, T::AccountId)>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.try_update_current(&new_commission)?; + bonded_pool.put(); + Self::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + current: new_commission, + }); + Ok(()) + } + + /// Set the maximum commission of a pool. + /// + /// The dispatch origin of this call must be signed by the `root` role of the pool. + #[pallet::call_index(15)] + #[pallet::weight(T::WeightInfo::set_commission_max())] + pub fn set_commission_max( + origin: OriginFor, + pool_id: PoolId, + max_commission: Perbill, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.try_update_max(max_commission)?; + bonded_pool.put(); + + Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); + Ok(()) + } + + /// Set the commission change rate for a pool. + /// + /// The dispatch origin of this call must be signed by the `root` role of the pool. + #[pallet::call_index(16)] + #[pallet::weight(T::WeightInfo::set_commission_change_rate())] + pub fn set_commission_change_rate( + origin: OriginFor, + pool_id: PoolId, + change_rate: CommissionChangeRate, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.try_update_change_rate(change_rate)?; + bonded_pool.put(); + + Self::deposit_event(Event::::PoolCommissionChangeRateUpdated { + pool_id, + change_rate, + }); + Ok(()) + } } #[pallet::hooks] @@ -2318,7 +2646,7 @@ impl Pallet { let current_reward_counter = reward_pool.current_reward_counter(bonded_pool.id, bonded_pool.points)?; - let pending_rewards = member.pending_rewards(current_reward_counter)?; + let mut pending_rewards = member.pending_rewards(current_reward_counter)?; if pending_rewards.is_zero() { return Ok(pending_rewards) @@ -2328,20 +2656,45 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - // Transfer payout to the member. - T::Currency::transfer( - &bonded_pool.reward_account(), - &member_account, - pending_rewards, - // defensive: the depositor has put existential deposit into the pool and it stays - // untouched, reward account shall not die. - ExistenceRequirement::AllowDeath, - )?; + // Gets the commission percentage and payee to be paid if commission has been set. + // Otherwise, `None` is returned. + let maybe_commission = &bonded_pool.commission.maybe_commission_and_payee(&pending_rewards); + + if let Some((pool_commission, payee)) = maybe_commission { + // Deduct any outstanding commission from the reward being claimed. + pending_rewards = pending_rewards.saturating_sub(*pool_commission); + + // Send any non-zero `pool_commission` to the commission `payee`. + if pool_commission > &Zero::zero() { + T::Currency::transfer( + &bonded_pool.reward_account(), + &payee, + *pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } + } + + // Transfer remaining payout to the member. + // + // In scenarios where commission is 100%, `pending_rewards` will be zero. We therefore check + // if there is a non-zero payout to be transferred. + if pending_rewards > Zero::zero() { + T::Currency::transfer( + &bonded_pool.reward_account(), + &member_account, + pending_rewards, + // defensive: the depositor has put existential deposit into the pool and it stays + // untouched, reward account shall not die. + ExistenceRequirement::KeepAlive, + )?; + } Self::deposit_event(Event::::PaidOut { member: member_account.clone(), pool_id: member.pool_id, payout: pending_rewards, + commission: maybe_commission.as_ref().map(|(c, _)| *c).unwrap_or(Zero::zero()), }); Ok(pending_rewards) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index b73141c95f72c..ad87eb0fbe45c 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -52,9 +52,12 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { + // Note: `commission` field not introduced to `BondedPoolInner` until + // migration 4. BondedPoolInner { - member_counter: self.member_counter, points: self.points, + commission: Commission::default(), + member_counter: self.member_counter, state: self.state, roles: self.roles.migrate_to_v1(), } @@ -283,6 +286,7 @@ pub mod v2 { member: who.clone(), pool_id: id, payout: last_claim, + commission: Zero::zero(), }); }); @@ -449,3 +453,88 @@ pub mod v3 { } } } + +pub mod v4 { + use super::*; + + #[derive(Decode)] + pub struct OldBondedPoolInner { + pub points: BalanceOf, + pub state: PoolState, + pub member_counter: u32, + pub roles: PoolRoles, + } + + impl OldBondedPoolInner { + fn migrate_to_v4(self) -> BondedPoolInner { + BondedPoolInner { + commission: Commission::default(), + member_counter: self.member_counter, + points: self.points, + state: self.state, + roles: self.roles, + } + } + } + + /// This migration adds a `commission` field to every `BondedPoolInner`, if + /// any. + pub struct MigrateToV4(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV4 { + fn on_runtime_upgrade() -> Weight { + let current = Pallet::::current_storage_version(); + let onchain = Pallet::::on_chain_storage_version(); + + log!( + info, + "Running migration with current storage version {:?} / onchain {:?}", + current, + onchain + ); + + if current == 4 && onchain == 3 { + GlobalMaxCommission::::set(Some(Zero::zero())); + log!(info, "Set initial global max commission to 0%"); + + let mut translated = 0u64; + BondedPools::::translate::, _>(|_key, old_value| { + translated.saturating_inc(); + Some(old_value.migrate_to_v4()) + }); + + current.put::>(); + log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); + + // reads: translated + onchain version. + // writes: translated + current.put + initial global commission. + T::DbWeight::get().reads_writes(translated + 1, translated + 2) + } else { + log!(info, "Migration did not execute. This probably should be removed"); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + ensure!( + Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), + "the on_chain version is equal or more than the current one" + ); + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), &'static str> { + // ensure all BondedPools items now contain an `inner.commission: Commission` field. + ensure!( + BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && + inner.commission.max.is_none() && + inner.commission.change_rate.is_none() && + inner.commission.throttle_from.is_none()), + "a commission value has been incorrectly set" + ); + ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); + Ok(()) + } + } +} diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 99d521df3241b..e779cae67814c 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -251,11 +251,17 @@ pub struct ExtBuilder { members: Vec<(AccountId, Balance)>, max_members: Option, max_members_per_pool: Option, + global_max_commission: Option, } impl Default for ExtBuilder { fn default() -> Self { - Self { members: Default::default(), max_members: Some(4), max_members_per_pool: Some(3) } + Self { + members: Default::default(), + max_members: Some(4), + max_members_per_pool: Some(3), + global_max_commission: Some(Perbill::from_percent(90)), + } } } @@ -297,6 +303,11 @@ impl ExtBuilder { self } + pub fn global_max_commission(mut self, commission: Option) -> Self { + self.global_max_commission = commission; + self + } + pub fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut storage = @@ -308,6 +319,7 @@ impl ExtBuilder { max_pools: Some(2), max_members_per_pool: self.max_members_per_pool, max_members: self.max_members, + global_max_commission: self.global_max_commission, } .assimilate_storage(&mut storage); @@ -354,6 +366,23 @@ parameter_types! { storage BalancesEvents: u32 = 0; } +/// Helper to run a specified amount of blocks. +pub fn run_blocks(n: u64) { + let current_block = System::block_number(); + run_to_block(n + current_block); +} + +/// Helper to run to a specific block. +pub fn run_to_block(n: u64) { + let current_block = System::block_number(); + assert!(n > current_block); + while System::block_number() < n { + Pools::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + Pools::on_initialize(System::block_number()); + } +} + /// All events of this pallet. pub fn pool_events_since_last_call() -> Vec> { let events = System::events() diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 7d5d418bbf2c8..4bd4973fc7704 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -3,17 +3,17 @@ // Copyright (C) 2020-2022 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 +// 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 +// 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. +// 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::*; use crate::{mock::*, Event}; @@ -55,10 +55,11 @@ fn test_setup_works() { BondedPool:: { id: last_pool, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, - roles: DEFAULT_ROLES + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, }, } ); @@ -98,10 +99,11 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -153,10 +155,11 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -201,10 +204,11 @@ mod bonded_pool { let pool = BondedPool:: { id: 123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -430,10 +434,11 @@ mod join { let bonded = |points, member_counter| BondedPool:: { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points, + commission: Commission::default(), member_counter, + points, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; ExtBuilder::default().with_check(0).build_and_execute(|| { @@ -513,10 +518,11 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { + commission: Commission::default(), member_counter: 1, - state: PoolState::Open, points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -582,10 +588,11 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -708,7 +715,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 },] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 },] ); // last recorded reward counter at the time of this member's payout is 1 assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 1)); @@ -724,7 +731,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del(40, 1)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 50)); @@ -737,7 +744,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del(50, 1)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 100)); @@ -753,7 +760,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 1.5)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 105)); @@ -766,7 +773,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 1.5)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 125)); @@ -783,7 +790,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 2.0)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 175)); @@ -796,7 +803,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 180)); @@ -813,7 +820,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] ); // We expect a payout of 40 @@ -832,7 +839,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2, commission: 0 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 6.2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 222)); @@ -845,7 +852,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188, commission: 0 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 6.2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 410)); @@ -858,7 +865,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210, commission: 0 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 6.2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 620)); @@ -890,6 +897,59 @@ mod claim_payout { }); } + #[test] + fn claim_payout_bounds_commission_above_global() { + ExtBuilder::default().build_and_execute(|| { + // temporarily remove global maximum so a higher commission can be set. + GlobalMaxCommission::::set(None); + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 75%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(75), 2)), + )); + + // re-introduce the global maximum to 50% - 25% lower than the current commission of the + // pool. + GlobalMaxCommission::::set(Some(Perbill::from_percent(50))); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // commission applied is 50%, not 75%. Has been bounded by `GlobalMaxCommission`. + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 5 },] + ); + }) + } + #[test] fn do_reward_payout_works_with_a_pool_of_1() { let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); @@ -922,7 +982,7 @@ mod claim_payout { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 5 } + Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 } ] ); assert_eq!(payout, 5); @@ -940,7 +1000,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); assert_eq!(payout, 10); assert_eq!(reward_pool, rew(0, 0, 15)); @@ -999,7 +1059,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); assert_eq!(payout, 10); assert_eq!(del_10, del(10, 1)); @@ -1013,7 +1073,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] ); assert_eq!(payout, 40); assert_eq!(del_40, del(40, 1)); @@ -1027,7 +1087,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(payout, 50); assert_eq!(del_50, del(50, 1)); @@ -1044,7 +1104,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(payout, 5); assert_eq!(del_10, del_float(10, 1.5)); @@ -1058,7 +1118,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] ); assert_eq!(payout, 20); assert_eq!(del_40, del_float(40, 1.5)); @@ -1075,7 +1135,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(payout, 50); assert_eq!(del_50, del_float(50, 2.0)); @@ -1089,7 +1149,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(payout, 5); assert_eq!(del_10, del_float(10, 2.0)); @@ -1106,7 +1166,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] ); assert_eq!(payout, 40); assert_eq!(del_10, del_float(10, 6.0)); @@ -1169,8 +1229,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, ] ); @@ -1183,8 +1243,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, ] ); }); @@ -1212,8 +1272,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 3 + 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 3 + 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, ] ); @@ -1226,8 +1286,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 4 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 4 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 4, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 4, commission: 0 }, ] ); @@ -1240,8 +1300,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, ] ); }); @@ -1276,9 +1336,19 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 30 + 100 / 2 + 60 / 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 100 / 2 + 60 / 3 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3 }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: 30 + 100 / 2 + 60 / 3, + commission: 0 + }, + Event::PaidOut { + member: 20, + pool_id: 1, + payout: 100 / 2 + 60 / 3, + commission: 0 + }, + Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3, commission: 0 }, ] ); @@ -1292,9 +1362,9 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 }, ] ); }); @@ -1377,9 +1447,9 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 10 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 } ] ); @@ -1397,9 +1467,9 @@ mod claim_payout { pool_events_since_last_call(), vec![ Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 40 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 40, commission: 0 } ] ); }); @@ -1425,8 +1495,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } ] ); @@ -1443,8 +1513,8 @@ mod claim_payout { pool_events_since_last_call(), vec![ Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 50 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 50 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 50, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 50, commission: 0 }, ] ); }); @@ -1474,8 +1544,8 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } ] ); @@ -1489,8 +1559,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } ] ); @@ -1504,8 +1574,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } ] ); @@ -1514,7 +1584,12 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 30, pool_id: 1, payout: 10 + 20 + 20 }] + vec![Event::PaidOut { + member: 30, + pool_id: 1, + payout: 10 + 20 + 20, + commission: 0 + }] ); }); } @@ -1539,7 +1614,7 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 } ] ); @@ -1557,9 +1632,9 @@ mod claim_payout { pool_events_since_last_call(), vec![ // 20 + 40, which means the extra amount they bonded did not impact us. - Event::PaidOut { member: 20, pool_id: 1, payout: 60 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 60, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 } ] ); @@ -1573,8 +1648,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 15 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 45 } + Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 45, commission: 0 } ] ); }); @@ -1641,7 +1716,7 @@ mod claim_payout { Event::Bonded { member: 20, pool_id: 2, bonded: 10, joined: true }, Event::Created { depositor: 30, pool_id: 3 }, Event::Bonded { member: 30, pool_id: 3, bonded: 10, joined: true }, - Event::PaidOut { member: 30, pool_id: 3, payout: 10 } + Event::PaidOut { member: 30, pool_id: 3, payout: 10, commission: 0 } ] ); }) @@ -1788,9 +1863,9 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 15 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 20, pool_id: 1, payout: 15 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 15, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false } ] ); @@ -1820,7 +1895,7 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); } @@ -1837,7 +1912,7 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 20, pool_id: 1, payout: 20 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: false } ] ); @@ -1934,12 +2009,12 @@ mod claim_payout { Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 20, joined: true }, Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 15 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 15, commission: 0 }, Event::Unbonded { member: 30, pool_id: 1, balance: 10, points: 10, era: 3 }, Event::Unbonded { member: 30, pool_id: 1, balance: 5, points: 5, era: 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 7 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 7, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, balance: 5, points: 5, era: 3 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 7 } + Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 0 } ] ); }) @@ -1970,8 +2045,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 13 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 26 } + Event::PaidOut { member: 10, pool_id: 1, payout: 13, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 26, commission: 0 } ] ); @@ -2056,10 +2131,10 @@ mod claim_payout { bonded: 5000000000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 100000000 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 150000000 }, - Event::PaidOut { member: 21, pool_id: 1, payout: 250000000 }, - Event::PaidOut { member: 22, pool_id: 1, payout: 500000000 } + Event::PaidOut { member: 10, pool_id: 1, payout: 100000000, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 150000000, commission: 0 }, + Event::PaidOut { member: 21, pool_id: 1, payout: 250000000, commission: 0 }, + Event::PaidOut { member: 22, pool_id: 1, payout: 500000000, commission: 0 } ] ); }) @@ -2359,10 +2434,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 0, + commission: Commission::default(), member_counter: 1, + points: 0, roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2395,10 +2471,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points: 560, + commission: Commission::default(), member_counter: 3, + points: 560, roles: DEFAULT_ROLES, + state: PoolState::Open, } } ); @@ -2409,7 +2486,7 @@ mod unbond { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, - Event::PaidOut { member: 40, pool_id: 1, payout: 40 }, + Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }, Event::Unbonded { member: 40, pool_id: 1, points: 6, balance: 6, era: 3 } ] ); @@ -2435,10 +2512,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 10, + commission: Commission::default(), member_counter: 3, - roles: DEFAULT_ROLES + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2451,7 +2529,7 @@ mod unbond { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 550, pool_id: 1, payout: 550 }, + Event::PaidOut { member: 550, pool_id: 1, payout: 550, commission: 0 }, Event::Unbonded { member: 550, pool_id: 1, @@ -2478,10 +2556,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 0, + commission: Commission::default(), member_counter: 1, - roles: DEFAULT_ROLES + points: 0, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2495,7 +2574,7 @@ mod unbond { Event::MemberRemoved { pool_id: 1, member: 40 }, Event::Withdrawn { member: 550, pool_id: 1, points: 92, balance: 92 }, Event::MemberRemoved { pool_id: 1, member: 550 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2, era: 6 } ] ); @@ -2606,10 +2685,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 10, // Only 10 points because 200 + 100 was unbonded roles: DEFAULT_ROLES, state: PoolState::Blocked, - points: 10, // Only 10 points because 200 + 100 was unbonded - member_counter: 3, } } ); @@ -2756,10 +2836,11 @@ mod unbond { BondedPool:: { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -3031,7 +3112,7 @@ mod unbond { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, balance: 2, points: 2, era: 3 } ] ); @@ -3047,7 +3128,7 @@ mod unbond { pool_events_since_last_call(), vec![ // 2/3 of ed, which is 20's share. - Event::PaidOut { member: 20, pool_id: 1, payout: 6 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 6, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3, era: 4 } ] ); @@ -3062,7 +3143,7 @@ mod unbond { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 20, pool_id: 1, payout: 3 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 5 } ] ); @@ -3468,10 +3549,11 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, points: 10, + roles: DEFAULT_ROLES, state: PoolState::Open, - member_counter: 3, - roles: DEFAULT_ROLES } } ); @@ -3548,10 +3630,11 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - points: 10, - state: PoolState::Open, + commission: Commission::default(), member_counter: 2, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, } } ); @@ -4101,15 +4184,16 @@ mod create { BondedPool { id: 2, inner: BondedPoolInner { + commission: Commission::default(), points: StakingMock::minimum_nominator_bond(), member_counter: 1, - state: PoolState::Open, roles: PoolRoles { depositor: 11, root: Some(123), nominator: Some(456), state_toggler: Some(789) - } + }, + state: PoolState::Open, } } ); @@ -4165,10 +4249,11 @@ mod create { BondedPool:: { id: 2, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -4411,12 +4496,14 @@ mod set_configs { ConfigOp::Set(3u32), ConfigOp::Set(4u32), ConfigOp::Set(5u32), + ConfigOp::Set(Perbill::from_percent(6)) )); assert_eq!(MinJoinBond::::get(), 1); assert_eq!(MinCreateBond::::get(), 2); assert_eq!(MaxPools::::get(), Some(3)); assert_eq!(MaxPoolMembers::::get(), Some(4)); assert_eq!(MaxPoolMembersPerPool::::get(), Some(5)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::from_percent(6))); // Noop does nothing assert_storage_noop!(assert_ok!(Pools::set_configs( @@ -4426,6 +4513,7 @@ mod set_configs { ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop, + ConfigOp::Noop, ))); // Removing works @@ -4436,12 +4524,14 @@ mod set_configs { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Remove, + ConfigOp::Remove, )); assert_eq!(MinJoinBond::::get(), 0); assert_eq!(MinCreateBond::::get(), 0); assert_eq!(MaxPools::::get(), None); assert_eq!(MaxPoolMembers::::get(), None); assert_eq!(MaxPoolMembersPerPool::::get(), None); + assert_eq!(GlobalMaxCommission::::get(), None); }); } } @@ -4520,7 +4610,12 @@ mod bond_extra { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: claimable_reward }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: claimable_reward, + commission: 0 + }, Event::Bonded { member: 10, pool_id: 1, @@ -4575,9 +4670,9 @@ mod bond_extra { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 1 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 0 }, Event::Bonded { member: 10, pool_id: 1, bonded: 1, joined: false }, - Event::PaidOut { member: 20, pool_id: 1, payout: 2 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 2, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 2, joined: false } ] ); @@ -4825,7 +4920,7 @@ mod reward_counter_precision { assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1173 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1173, commission: 0 }] ); }) } @@ -4858,8 +4953,18 @@ mod reward_counter_precision { pool_events_since_last_call(), vec![ Event::Bonded { member: 20, pool_id: 1, bonded: 5000000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 7333333333333333333 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3666666666666666666 } + Event::PaidOut { + member: 10, + pool_id: 1, + payout: 7333333333333333333, + commission: 0 + }, + Event::PaidOut { + member: 20, + pool_id: 1, + payout: 3666666666666666666, + commission: 0 + } ] ); }) @@ -4910,7 +5015,12 @@ mod reward_counter_precision { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 15937424600999999996 }] + vec![Event::PaidOut { + member: 10, + pool_id: 1, + payout: 15937424600999999996, + commission: 0 + }] ); // now let a small member join with 10 DOTs. @@ -4926,7 +5036,7 @@ mod reward_counter_precision { vec![ Event::Bonded { member: 30, pool_id: 1, bonded: 100000000000, joined: true }, // quite small, but working fine. - Event::PaidOut { member: 30, pool_id: 1, payout: 38 } + Event::PaidOut { member: 30, pool_id: 1, payout: 38, commission: 0 } ] ); }) @@ -5006,7 +5116,7 @@ mod reward_counter_precision { bonded: 100000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 9999997 } + Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } ] ); @@ -5019,7 +5129,12 @@ mod reward_counter_precision { assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10000000 }] + vec![Event::PaidOut { + member: 10, + pool_id: 1, + payout: 10000000, + commission: 0 + }] ); // earn some more rewards, this time 20 can also claim. @@ -5032,8 +5147,8 @@ mod reward_counter_precision { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10000000 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 1 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10000000, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 1, commission: 0 } ] ); }); @@ -5082,7 +5197,7 @@ mod reward_counter_precision { bonded: 100000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 9999997 } + Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } ] ); @@ -5100,3 +5215,1000 @@ mod reward_counter_precision { }); } } + +mod commission { + use super::*; + + #[test] + fn set_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // Set a commission for pool 1. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(50), 900)) + )); + + let commission_as_percent = BondedPool::::get(1) + .unwrap() + .commission + .current + .as_ref() + .map(|(x, _)| *x) + .unwrap_or(Perbill::zero()); + assert_eq!(commission_as_percent, Perbill::from_percent(50)); + + // update commission only. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(25), 900)) + )); + + // update payee only. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(25), 901)) + )); + + // remove the commission for pool 1. + assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None)); + + // test whether supplying a 0% commission along with a payee results in a None `current` + // being inserted. + // + // set an initial commission of 10% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + )); + // set the commission to 0% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(0), 900)) + )); + // commssion current should now be None, and `throttle_from` the current block. + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { current: None, max: None, change_rate: None, throttle_from: Some(1) } + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(50), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(25), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(25), 901)) + }, + Event::PoolCommissionUpdated { pool_id: 1, current: None }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(0), 900)) + } + ] + ); + }); + } + + #[test] + fn set_commission_handles_errors() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 9999, + Some((Perbill::from_percent(1), 900)), + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(1), + 1, + Some((Perbill::from_percent(5), 900)), + ), + Error::::DoesNotHavePermission + ); + + // Commission clamps to 100% where > 100% is provided. + // + // attempt to set a commission at 101%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(101), 900)), + )); + // ensure the commission actually set was at 100%. + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(100), 900)), + max: None, + change_rate: None, + throttle_from: Some(1_u64), + } + ); + + // Set the initial commission to 5%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)), + )); + + // Change rate test. + // + // Set a change rate to be a +1% commission increase every 2 blocks. + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(5), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }), + throttle_from: Some(1_u64), + } + ); + + // Now try to increase commission to 10% (5% increase). This should be throttled. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + ), + Error::::CommissionChangeThrottled + ); + + // Run to block 3 + run_blocks(2); + + // Now try to increase commission by 1% and provide an initial payee. This should + // succeed and set the `throttle_from` field. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(6), 900)) + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(6), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }), + throttle_from: Some(3_u64), + } + ); + + // Attempt to increase the commission an additional 1% (now 7%). this will fail as + // `throttle_from` is now the current block. At least 2 blocks need to pass before we + // can set commission again. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(7), 900)) + ), + Error::::CommissionChangeThrottled + ); + + // Run 2 blocks into the future, to block 3. + run_blocks(2); + + // Can now successfully increase the commission again, to 7%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(7), 900)), + )); + + // Run 2 blocks into the future, to block 5. + run_blocks(2); + + // Now surpassed the `min_delay` threshold, but the `max_increase` threshold is + // still at play. An attempted commission change now to 8% (+2% increase) should fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(9), 900)), + ), + Error::::CommissionChangeThrottled + ); + + // Now set a max commission to the current 5%. This will also update the current + // commission to 5%. + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5) + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(5), 900)), + max: Some(Perbill::from_percent(5)), + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2 + }), + throttle_from: Some(7) + } + ); + + // Run 2 blocks into the future so we are eligible to update commission again. + run_blocks(2); + + // Now attempt again to increase the commission by 1%, to 6%. This is within the + // change rate allowance, but `max_commission` will now prevent us from going any + // higher. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(6), 900)), + ), + Error::::CommissionExceedsMaximum + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(5), 900)) + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2 + } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(6), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(7), 900)) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(5) + } + ] + ); + }); + } + + #[test] + fn set_commission_max_works_with_error_tests() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_max( + RuntimeOrigin::signed(900), + 9999, + Perbill::from_percent(1) + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Error::::DoesNotHavePermission + ); + + // Max commission clamps to 100% where > 100% is provided. + // + // attempt to set a commission at 101%. + + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(101) + )); + // ensure max commission actually set was at 100%. + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: None, + max: Some(Perbill::from_percent(100)), + change_rate: None, + throttle_from: None, + } + ); + + // Set a max commission commission pool 1 to 80% + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(80) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.max, + Some(Perbill::from_percent(80)) + ); + + // We attempt to increase the max commission to 90%, but increasing is + // disallowed due to pool's max commission. + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(90)), + Error::::MaxCommissionRestricted + ); + + // We will now set a commission to 75% and then amend the max commission + // to 50%. The max commission change should decrease the current + // commission to 50%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(75), 900)) + )); + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(50) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(50), 900)), + max: Some(Perbill::from_percent(50)), + change_rate: None, + throttle_from: Some(1), + } + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(100) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(80) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 900)), + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(50) + } + ] + ); + }); + } + + #[test] + fn max_commission_after_current_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // set pool commission to 50% first. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(50), 900)), + )); + + // now set the max commission to something less than the current + // commission. + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(25) + )); + + // the current commission should now be 25%. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(25), 900)), + max: Some(Perbill::from_percent(25)), + change_rate: None, + throttle_from: Some(1), + } + ); + }) + } + + #[test] + fn set_commission_change_rate_works_with_errors() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 9999, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(1), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } + ), + Error::::DoesNotHavePermission + ); + + // Set a commission change rate for pool 1 + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.change_rate, + Some(CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10_u64 + }) + ); + + // We now try to half the min_delay - this will be disallowed. A greater delay between + // commission changes is seen as more restrictive. + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 5_u64 + } + ), + Error::::CommissionChangeRateNotAllowed + ); + + // We now try to increase the allowed max_increase - this will fail. A smaller allowed + // commission change is seen as more restrictive. + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(10), + min_delay: 10_u64 + } + ), + Error::::CommissionChangeRateNotAllowed + ); + + // Successful more restrictive change of min_delay with the current max_increase + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } + )); + + // Successful more restrictive change of max_increase with the current min_delay + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(4), min_delay: 20_u64 } + )); + + // Successful more restrictive change of both max_increase and min_delay + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } + )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 20 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(4), + min_delay: 20 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(3), + min_delay: 30 + } + } + ] + ); + }); + } + + #[test] + fn change_rate_does_not_apply_to_decreasing_commission() { + ExtBuilder::default().build_and_execute(|| { + // set initial commission of the pool to 10%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + )); + + // Set a commission change rate for pool 1, 1% every 10 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 10_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.change_rate, + Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10_u64 + }) + ); + + // run `min_delay` blocks to allow a commission update. + run_blocks(10_u64); + + // Test `max_increase`: attempt to decrease the commission by 5%. Should succeed. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)) + )); + + // Test `min_delay`: *immediately* attempt to decrease the commission by 2%. Should + // succeed. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)) + )); + + // run `min_delay` blocks to allow a commission update. + run_blocks(10_u64); + + // Attempt to *increase* the commission by 5%. Should fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(8), 900)) + ), + Error::::CommissionChangeThrottled + ); + + // Sanity check: the resulting pool Commission state. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(3), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10_u64 + }), + throttle_from: Some(11), + } + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 900)) + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10 + } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(5), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(3), 900)) + } + ] + ); + }); + } + + #[test] + fn set_commission_max_to_zero_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% max commission test. + // + // set commission max 0%. + assert_ok!(Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Zero::zero())); + + // a max commission of 0% essentially freezes the current commission, even when None. + // All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionExceedsMaximum + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_max_increase_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% max increase test. + // + // set commission change rate to 0% per 10 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 10_u64 } + )); + + // even though there is a min delay of 10 blocks, a max increase of 0% essentially + // freezes the commission. All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionChangeThrottled + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_min_delay_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% min delay test. + // + // set commission change rate to 1% with a 0 block `min_delay`. + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 0_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: None, + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 0 + }), + throttle_from: Some(1) + } + ); + + // since there is no min delay, we should be able to immediately set the commission. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + )); + + // sanity check: increasing again to more than +1% will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)) + ), + Error::::CommissionChangeThrottled + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_value_works() { + ExtBuilder::default().build_and_execute(|| { + // Check zero values play nice. 0 `min_delay` and 0% max_increase test. + // + // set commission change rate to 0% per 0 blocks. + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 0_u64 } + )); + + // even though there is no min delay, a max increase of 0% essentially freezes the + // commission. All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionChangeThrottled + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(0), + min_delay: 0_u64 + } + } + ] + ); + }) + } + + #[test] + fn do_reward_payout_with_various_commissions() { + ExtBuilder::default().build_and_execute(|| { + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 33%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(33), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(33), 2)) + }, + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] + ); + + // The pool earns 17 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] + ); + + // The pool earns 50 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] + ); + + // The pool earns 10439 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994, commission: 3445 },] + ); + }) + } + + #[test] + fn do_reward_payout_with_100_percent_commission() { + ExtBuilder::default().build_and_execute(|| { + // turn off GlobalMaxCommission for this test. + GlobalMaxCommission::::set(None); + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 100%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(100), 2)), + )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + // Ensure the commission equals the total amount of points. + let maybe_commission = &BondedPools::::get(1) + .unwrap() + .commission + .maybe_commission_and_payee(&10); + assert_eq!(*maybe_commission, Some((10_u128, 2_u128))); + + // execute the payout + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 0, commission: 10 },] + ); + }) + } + + #[test] + fn global_max_prevents_100_percent_commission_payout() { + ExtBuilder::default().build_and_execute(|| { + // Note: GlobalMaxCommission is set at 90%. + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up the commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 100%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(100), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + // Ensure the commission equals 90% of the total points. + let maybe_commission = &BondedPools::::get(1) + .unwrap() + .commission + .maybe_commission_and_payee(&10); + assert_eq!(*maybe_commission, Some((9_u128, 2_u128))); + + // execute the payout + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Confirm the commission was only 9 points out of 10 points, and the payout was 1 out + // of 10 points, reflecting the 90% global max commission. + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 9 },] + ); + }) + } +} diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 1062b1749d417..bc5471b6cf2b6 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -1,42 +1,32 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 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. //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2022-12-27, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Rosss-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// target/release/substrate // benchmark // pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_nomination_pools -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./frame/nomination-pools/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --execution +// wasm +// --wasm-execution +// compiled +// --dev +// --pallet +// pallet-nomination-pools +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --output +// frame/nomination-pools/src/weights.rs +// --template +// .maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -62,6 +52,9 @@ pub trait WeightInfo { fn set_configs() -> Weight; fn update_roles() -> Weight; fn chill() -> Weight; + fn set_commission() -> Weight; + fn set_commission_max() -> Weight; + fn set_commission_change_rate() -> Weight; } /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware. @@ -81,10 +74,10 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 159_948 nanoseconds. - Weight::from_ref_time(161_133_000 as u64) - .saturating_add(T::DbWeight::get().reads(17 as u64)) - .saturating_add(T::DbWeight::get().writes(12 as u64)) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(145_000_000) + .saturating_add(T::DbWeight::get().reads(17)) + .saturating_add(T::DbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -96,35 +89,37 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 155_517 nanoseconds. - Weight::from_ref_time(159_101_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(12 as u64)) + // Minimum execution time: 137_000 nanoseconds. + Weight::from_ref_time(142_000_000) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:3) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 172_788 nanoseconds. - Weight::from_ref_time(174_212_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(13 as u64)) + // Minimum execution time: 166_000 nanoseconds. + Weight::from_ref_time(169_000_000) + .saturating_add(T::DbWeight::get().reads(15)) + .saturating_add(T::DbWeight::get().writes(13)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn claim_payout() -> Weight { - // Minimum execution time: 64_560 nanoseconds. - Weight::from_ref_time(64_950_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Minimum execution time: 64_000 nanoseconds. + Weight::from_ref_time(65_000_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -141,10 +136,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 161_398 nanoseconds. - Weight::from_ref_time(162_991_000 as u64) - .saturating_add(T::DbWeight::get().reads(18 as u64)) - .saturating_add(T::DbWeight::get().writes(13 as u64)) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(150_000_000) + .saturating_add(T::DbWeight::get().reads(18)) + .saturating_add(T::DbWeight::get().writes(13)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -153,12 +148,12 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 66_036 nanoseconds. - Weight::from_ref_time(67_183_304 as u64) - // Standard Error: 565 - .saturating_add(Weight::from_ref_time(57_830 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(53_724_243) + // Standard Error: 1_649 + .saturating_add(Weight::from_ref_time(23_177).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -171,12 +166,12 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 111_156 nanoseconds. - Weight::from_ref_time(112_507_059 as u64) - // Standard Error: 655 - .saturating_add(Weight::from_ref_time(53_711 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(9 as u64)) - .saturating_add(T::DbWeight::get().writes(7 as u64)) + // Minimum execution time: 95_000 nanoseconds. + Weight::from_ref_time(99_545_463) + // Standard Error: 5_079 + .saturating_add(Weight::from_ref_time(31_957).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(7)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -199,13 +194,11 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 168_270 nanoseconds. - Weight::from_ref_time(170_059_380 as u64) - // Standard Error: 1_506 - .saturating_add(Weight::from_ref_time(1_258 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(20 as u64)) - .saturating_add(T::DbWeight::get().writes(17 as u64)) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(153_276_873) + .saturating_add(T::DbWeight::get().reads(20)) + .saturating_add(T::DbWeight::get().writes(17)) } // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: Staking MinNominatorBond (r:1 w:0) @@ -229,10 +222,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 146_153 nanoseconds. - Weight::from_ref_time(146_955_000 as u64) - .saturating_add(T::DbWeight::get().reads(21 as u64)) - .saturating_add(T::DbWeight::get().writes(15 as u64)) + // Minimum execution time: 131_000 nanoseconds. + Weight::from_ref_time(132_000_000) + .saturating_add(T::DbWeight::get().reads(21)) + .saturating_add(T::DbWeight::get().writes(15)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -248,51 +241,52 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 71_380 nanoseconds. - Weight::from_ref_time(71_060_388 as u64) - // Standard Error: 2_587 - .saturating_add(Weight::from_ref_time(1_185_729 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(12 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(58_398_812) + // Standard Error: 7_443 + .saturating_add(Weight::from_ref_time(1_436_557).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 46_275 nanoseconds. - Weight::from_ref_time(46_689_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(35_000_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 19_246 nanoseconds. - Weight::from_ref_time(20_415_018 as u64) - // Standard Error: 95 - .saturating_add(Weight::from_ref_time(2_040 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_360_093) + // Standard Error: 294 + .saturating_add(Weight::from_ref_time(514).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } // Storage: NominationPools MinJoinBond (r:0 w:1) // Storage: NominationPools MaxPoolMembers (r:0 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) // Storage: NominationPools MinCreateBond (r:0 w:1) + // Storage: NominationPools GlobalMaxCommission (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 9_231 nanoseconds. - Weight::from_ref_time(9_526_000 as u64) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(T::DbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 31_246 nanoseconds. - Weight::from_ref_time(31_762_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -304,10 +298,31 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 73_812 nanoseconds. - Weight::from_ref_time(74_790_000 as u64) - .saturating_add(T::DbWeight::get().reads(9 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(60_000_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission() -> Weight { + // Minimum execution time: 24_000 nanoseconds. + Weight::from_ref_time(24_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_max() -> Weight { + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_change_rate() -> Weight { + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } } @@ -327,10 +342,10 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 159_948 nanoseconds. - Weight::from_ref_time(161_133_000 as u64) - .saturating_add(RocksDbWeight::get().reads(17 as u64)) - .saturating_add(RocksDbWeight::get().writes(12 as u64)) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(145_000_000) + .saturating_add(RocksDbWeight::get().reads(17)) + .saturating_add(RocksDbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -342,35 +357,37 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 155_517 nanoseconds. - Weight::from_ref_time(159_101_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(12 as u64)) + // Minimum execution time: 137_000 nanoseconds. + Weight::from_ref_time(142_000_000) + .saturating_add(RocksDbWeight::get().reads(14)) + .saturating_add(RocksDbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:3) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 172_788 nanoseconds. - Weight::from_ref_time(174_212_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(13 as u64)) + // Minimum execution time: 166_000 nanoseconds. + Weight::from_ref_time(169_000_000) + .saturating_add(RocksDbWeight::get().reads(15)) + .saturating_add(RocksDbWeight::get().writes(13)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn claim_payout() -> Weight { - // Minimum execution time: 64_560 nanoseconds. - Weight::from_ref_time(64_950_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Minimum execution time: 64_000 nanoseconds. + Weight::from_ref_time(65_000_000) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(4)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -387,10 +404,10 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 161_398 nanoseconds. - Weight::from_ref_time(162_991_000 as u64) - .saturating_add(RocksDbWeight::get().reads(18 as u64)) - .saturating_add(RocksDbWeight::get().writes(13 as u64)) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(150_000_000) + .saturating_add(RocksDbWeight::get().reads(18)) + .saturating_add(RocksDbWeight::get().writes(13)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -399,12 +416,12 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 66_036 nanoseconds. - Weight::from_ref_time(67_183_304 as u64) - // Standard Error: 565 - .saturating_add(Weight::from_ref_time(57_830 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(53_724_243) + // Standard Error: 1_649 + .saturating_add(Weight::from_ref_time(23_177).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -417,12 +434,12 @@ impl WeightInfo for () { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 111_156 nanoseconds. - Weight::from_ref_time(112_507_059 as u64) - // Standard Error: 655 - .saturating_add(Weight::from_ref_time(53_711 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(9 as u64)) - .saturating_add(RocksDbWeight::get().writes(7 as u64)) + // Minimum execution time: 95_000 nanoseconds. + Weight::from_ref_time(99_545_463) + // Standard Error: 5_079 + .saturating_add(Weight::from_ref_time(31_957).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(9)) + .saturating_add(RocksDbWeight::get().writes(7)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -445,13 +462,11 @@ impl WeightInfo for () { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 168_270 nanoseconds. - Weight::from_ref_time(170_059_380 as u64) - // Standard Error: 1_506 - .saturating_add(Weight::from_ref_time(1_258 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(20 as u64)) - .saturating_add(RocksDbWeight::get().writes(17 as u64)) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(153_276_873) + .saturating_add(RocksDbWeight::get().reads(20)) + .saturating_add(RocksDbWeight::get().writes(17)) } // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: Staking MinNominatorBond (r:1 w:0) @@ -475,10 +490,10 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 146_153 nanoseconds. - Weight::from_ref_time(146_955_000 as u64) - .saturating_add(RocksDbWeight::get().reads(21 as u64)) - .saturating_add(RocksDbWeight::get().writes(15 as u64)) + // Minimum execution time: 131_000 nanoseconds. + Weight::from_ref_time(132_000_000) + .saturating_add(RocksDbWeight::get().reads(21)) + .saturating_add(RocksDbWeight::get().writes(15)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -494,51 +509,52 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 71_380 nanoseconds. - Weight::from_ref_time(71_060_388 as u64) - // Standard Error: 2_587 - .saturating_add(Weight::from_ref_time(1_185_729 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(12 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(58_398_812) + // Standard Error: 7_443 + .saturating_add(Weight::from_ref_time(1_436_557).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 46_275 nanoseconds. - Weight::from_ref_time(46_689_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(35_000_000) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 19_246 nanoseconds. - Weight::from_ref_time(20_415_018 as u64) - // Standard Error: 95 - .saturating_add(Weight::from_ref_time(2_040 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_360_093) + // Standard Error: 294 + .saturating_add(Weight::from_ref_time(514).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(2)) } // Storage: NominationPools MinJoinBond (r:0 w:1) // Storage: NominationPools MaxPoolMembers (r:0 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) // Storage: NominationPools MinCreateBond (r:0 w:1) + // Storage: NominationPools GlobalMaxCommission (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 9_231 nanoseconds. - Weight::from_ref_time(9_526_000 as u64) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 31_246 nanoseconds. - Weight::from_ref_time(31_762_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -550,9 +566,30 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 73_812 nanoseconds. - Weight::from_ref_time(74_790_000 as u64) - .saturating_add(RocksDbWeight::get().reads(9 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(60_000_000) + .saturating_add(RocksDbWeight::get().reads(9)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission() -> Weight { + // Minimum execution time: 24_000 nanoseconds. + Weight::from_ref_time(24_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_max() -> Weight { + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_change_rate() -> Weight { + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } } diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index c67aec0134b07..fb3aef130a77d 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ }; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128, + FixedU128, Perbill, }; type AccountId = u128; @@ -209,6 +209,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(5), max_members: Some(3 * 5), + global_max_commission: Some(Perbill::from_percent(90)), } .assimilate_storage(&mut storage) .unwrap(); diff --git a/frame/session/src/migrations/v1.rs b/frame/session/src/migrations/v1.rs index c0dce422fe8b5..6689ca7299f1e 100644 --- a/frame/session/src/migrations/v1.rs +++ b/frame/session/src/migrations/v1.rs @@ -29,6 +29,8 @@ use frame_support::{ use crate::historical as pallet_session_historical; +const LOG_TARGET: &str = "runtime::session_historical"; + const OLD_PREFIX: &str = "Session"; /// Migrate the entire storage of this pallet to a new prefix. @@ -44,7 +46,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::session_historical", + target: LOG_TARGET, "Running migration to v1 for session_historical with storage version {:?}", on_chain_storage_version, ); @@ -78,7 +80,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::session_historical", + target: LOG_TARGET, "Attempted to apply migration to v1 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -184,7 +186,7 @@ pub fn post_migrate< fn log_migration(stage: &str, storage_prefix: &[u8], old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::session_historical", + target: LOG_TARGET, "{} prefix of storage '{}': '{}' ==> '{}'", stage, str::from_utf8(storage_prefix).unwrap_or(""), diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index db9aeba6fb58e..7af9b0aaaa04a 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -339,7 +339,7 @@ impl Pallet { if maybe_new_era_validators.is_some() && matches!(ForceEra::::get(), Forcing::ForceNew) { - ForceEra::::put(Forcing::NotForcing); + Self::set_force_era(Forcing::NotForcing); } maybe_new_era_validators @@ -717,11 +717,18 @@ impl Pallet { } } + /// Helper to set a new `ForceEra` mode. + pub(crate) fn set_force_era(mode: Forcing) { + log!(info, "Setting force era mode {:?}.", mode); + ForceEra::::put(mode); + Self::deposit_event(Event::::ForceEra { mode }); + } + /// Ensures that at the end of the current session there will be a new era. pub(crate) fn ensure_new_era() { match ForceEra::::get() { Forcing::ForceAlways | Forcing::ForceNew => (), - _ => ForceEra::::put(Forcing::ForceNew), + _ => Self::set_force_era(Forcing::ForceNew), } } diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 92502949ef1a0..c0c18b40cf02f 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -292,6 +292,8 @@ pub mod pallet { pub type Invulnerables = StorageValue<_, Vec, ValueQuery>; /// Map from all locked "stash" accounts to the controller account. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn bonded)] pub type Bonded = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; @@ -320,12 +322,16 @@ pub mod pallet { pub type Ledger = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger>; /// Where the reward payment should be made. Keyed by stash. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn payee)] pub type Payee = StorageMap<_, Twox64Concat, T::AccountId, RewardDestination, ValueQuery>; /// The map from (wannabe) validator stash key to the preferences of that validator. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn validators)] pub type Validators = @@ -353,6 +359,8 @@ pub mod pallet { /// /// Lastly, if any of the nominators become non-decodable, they can be chilled immediately via /// [`Call::chill_other`] dispatchable by anyone. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn nominators)] pub type Nominators = @@ -706,6 +714,8 @@ pub mod pallet { PayoutStarted { era_index: EraIndex, validator_stash: T::AccountId }, /// A validator has set their preferences. ValidatorPrefsSet { stash: T::AccountId, prefs: ValidatorPrefs }, + /// A new force era mode was set. + ForceEra { mode: Forcing }, } #[pallet::error] @@ -1369,7 +1379,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_no_eras())] pub fn force_no_eras(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::::put(Forcing::ForceNone); + Self::set_force_era(Forcing::ForceNone); Ok(()) } @@ -1393,7 +1403,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_new_era())] pub fn force_new_era(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::::put(Forcing::ForceNew); + Self::set_force_era(Forcing::ForceNew); Ok(()) } @@ -1444,7 +1454,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_new_era_always())] pub fn force_new_era_always(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::::put(Forcing::ForceAlways); + Self::set_force_era(Forcing::ForceAlways); Ok(()) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 46c3c97441938..acd41895287c5 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -899,7 +899,7 @@ fn forcing_new_era_works() { assert_eq!(active_era(), 1); // no era change. - ForceEra::::put(Forcing::ForceNone); + Staking::set_force_era(Forcing::ForceNone); start_session(4); assert_eq!(active_era(), 1); @@ -915,7 +915,7 @@ fn forcing_new_era_works() { // back to normal. // this immediately starts a new session. - ForceEra::::put(Forcing::NotForcing); + Staking::set_force_era(Forcing::NotForcing); start_session(8); assert_eq!(active_era(), 1); @@ -923,7 +923,7 @@ fn forcing_new_era_works() { start_session(9); assert_eq!(active_era(), 2); // forceful change - ForceEra::::put(Forcing::ForceAlways); + Staking::set_force_era(Forcing::ForceAlways); start_session(10); assert_eq!(active_era(), 2); @@ -935,7 +935,7 @@ fn forcing_new_era_works() { assert_eq!(active_era(), 4); // just one forceful change - ForceEra::::put(Forcing::ForceNew); + Staking::set_force_era(Forcing::ForceNew); start_session(13); assert_eq!(active_era(), 5); assert_eq!(ForceEra::::get(), Forcing::NotForcing); @@ -2303,7 +2303,7 @@ fn era_is_always_same_length() { ); let session = Session::current_index(); - ForceEra::::put(Forcing::ForceNew); + Staking::set_force_era(Forcing::ForceNew); advance_session(); advance_session(); assert_eq!(current_era(), 3); @@ -2914,7 +2914,10 @@ fn deferred_slashes_are_deferred() { staking_events_since_last_call().as_slice(), &[ Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, slash_era: 1, .. }, + Event::StakersElected, + Event::ForceEra { mode: Forcing::NotForcing }, .., Event::Slashed { staker: 11, amount: 100 }, Event::Slashed { staker: 101, amount: 12 } @@ -2949,6 +2952,7 @@ fn retroactive_deferred_slashes_two_eras_before() { staking_events_since_last_call().as_slice(), &[ Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, slash_era: 1, .. }, .., Event::Slashed { staker: 11, amount: 100 }, @@ -3251,6 +3255,7 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid Event::StakersElected, Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, fraction: Perbill::from_percent(10), @@ -3318,6 +3323,7 @@ fn non_slashable_offence_doesnt_disable_validator() { Event::StakersElected, Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, fraction: Perbill::from_percent(0), @@ -3380,6 +3386,7 @@ fn slashing_independent_of_disabling_validator() { Event::StakersElected, Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, fraction: Perbill::from_percent(0), @@ -4662,8 +4669,15 @@ mod election_data_provider { 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::StakersElected); + assert_eq!(staking_events().len(), 10); + assert_eq!( + *staking_events().last().unwrap(), + Event::ForceEra { mode: Forcing::NotForcing } + ); + assert_eq!( + *staking_events().get(staking_events().len() - 2).unwrap(), + Event::StakersElected + ); // The new era has been planned, forcing is changed from `ForceNew` to `NotForcing`. assert_eq!(ForceEra::::get(), Forcing::NotForcing); }) diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index 0aa7c1e7aaf06..c2b70c5632f94 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -63,7 +63,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo - >::name::().expect("Every active pallet has a name in the runtime; qed"); + >::name::().expect("No name found for the pallet! This usually means that the pallet wasn't added to `construct_runtime!`."); #frame_support::log::debug!( target: #frame_support::LOG_TARGET, "🩺 try-state pallet {:?}", diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 181f35b545496..195a62431f279 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -535,7 +535,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo >::name::>() - .expect("Every active pallet has a name in the runtime; qed") + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") } const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const; } @@ -569,7 +569,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo >::name::>() - .expect("Every active pallet has a name in the runtime; qed") + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") } const STORAGE_PREFIX: &'static str = #prefix_struct_const; } @@ -648,7 +648,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo >::name::<#pallet_ident<#type_use_gen>>() - .expect("Every active pallet has a name in the runtime; qed"), + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), entries: { #[allow(unused_mut)] let mut entries = #frame_support::sp_std::vec![]; diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr index db96b8749ca11..0a20cf4e39a88 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr @@ -1,5 +1,5 @@ error: Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,` - --> $DIR/invalid_module_details.rs:9:17 + --> tests/construct_runtime_ui/invalid_module_details.rs:9:17 | 9 | system: System::(), - | ^^ + | ^ diff --git a/frame/support/test/tests/derive_no_bound.rs b/frame/support/test/tests/derive_no_bound.rs index f891b3a2d2db8..9162b5013bcdc 100644 --- a/frame/support/test/tests/derive_no_bound.rs +++ b/frame/support/test/tests/derive_no_bound.rs @@ -72,7 +72,7 @@ fn test_struct_named() { assert_eq!(a_2, a_1); assert_eq!( format!("{:?}", a_1), - String::from("StructNamed { a: 1, b: 2, c: 3, phantom: PhantomData }") + String::from("StructNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }") ); let b = StructNamed:: { @@ -103,7 +103,7 @@ fn test_struct_unnamed() { assert_eq!(a_2.1, 2); assert_eq!(a_2.2, 3); assert_eq!(a_2, a_1); - assert_eq!(format!("{:?}", a_1), String::from("StructUnnamed(1, 2, 3, PhantomData)")); + assert_eq!(format!("{:?}", a_1), String::from("StructUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)")); let b = StructUnnamed::(1, 2, 4, Default::default()); @@ -211,11 +211,11 @@ fn test_enum() { assert_eq!( format!("{:?}", variant_0), - String::from("Enum::VariantUnnamed(1, 2, 3, PhantomData)"), + String::from("Enum::VariantUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)"), ); assert_eq!( format!("{:?}", variant_1), - String::from("Enum::VariantNamed { a: 1, b: 2, c: 3, phantom: PhantomData }"), + String::from("Enum::VariantNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }"), ); assert_eq!(format!("{:?}", variant_2), String::from("Enum::VariantUnit")); assert_eq!(format!("{:?}", variant_3), String::from("Enum::VariantUnit2")); diff --git a/frame/support/test/tests/pallet_ui/call_invalid_return.stderr b/frame/support/test/tests/pallet_ui/call_invalid_return.stderr index 6a851ed3fc283..8803bbba01326 100644 --- a/frame/support/test/tests/pallet_ui/call_invalid_return.stderr +++ b/frame/support/test/tests/pallet_ui/call_invalid_return.stderr @@ -1,5 +1,5 @@ error: expected `DispatchResultWithPostInfo` or `DispatchResult` - --> $DIR/call_invalid_return.rs:17:39 + --> tests/pallet_ui/call_invalid_return.rs:17:39 | 17 | pub fn foo(origin: OriginFor) -> ::DispatchResult { todo!() } - | ^^ + | ^ diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index c790d33adddd7..3909b1e9c5257 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -129,6 +129,8 @@ pub use extensions::check_mortality::CheckMortality as CheckEra; pub use frame_support::dispatch::RawOrigin; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::system"; + /// Compute the trie root of a list of extrinsics. /// /// The merkle proof is using the same trie as runtime state with @@ -1075,7 +1077,7 @@ impl Pallet { if account.providers == 0 { // Logic error - cannot decrement beyond zero. log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Unexpected underflow in reducing provider", ); account.providers = 1; @@ -1101,7 +1103,7 @@ impl Pallet { } } else { log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Account already dead when reducing provider", ); Ok(DecRefStatus::Reaped) @@ -1133,7 +1135,7 @@ impl Pallet { if account.sufficients == 0 { // Logic error - cannot decrement beyond zero. log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Unexpected underflow in reducing sufficients", ); } @@ -1150,7 +1152,7 @@ impl Pallet { } } else { log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Account already dead when reducing provider", ); DecRefStatus::Reaped @@ -1215,7 +1217,7 @@ impl Pallet { a.consumers -= 1; } else { log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Unexpected underflow in reducing consumer", ); } @@ -1337,7 +1339,7 @@ impl Pallet { /// resulting header for this block. pub fn finalize() -> T::Header { log::debug!( - target: "runtime::system", + target: LOG_TARGET, "[{:?}] {} extrinsics, length: {} (normal {}%, op: {}%, mandatory {}%) / normal weight:\ {} ({}%) op weight {} ({}%) / mandatory weight {} ({}%)", Self::block_number(), @@ -1547,7 +1549,7 @@ impl Pallet { Ok(_) => Event::ExtrinsicSuccess { dispatch_info: info }, Err(err) => { log::trace!( - target: "runtime::system", + target: LOG_TARGET, "Extrinsic failed at block({:?}): {:?}", Self::block_number(), err, diff --git a/frame/system/src/migrations/mod.rs b/frame/system/src/migrations/mod.rs index 15746d7376ac5..90b88de1ab11c 100644 --- a/frame/system/src/migrations/mod.rs +++ b/frame/system/src/migrations/mod.rs @@ -17,6 +17,7 @@ //! Migrate the reference counting state. +use super::LOG_TARGET; use crate::{Config, Pallet}; use codec::{Decode, Encode, FullCodec}; use frame_support::{ @@ -75,7 +76,7 @@ pub fn migrate_from_single_u8_to_triple_ref_count() -> Wei Some(AccountInfo { nonce, consumers: rc as RefCount, providers: 1, sufficients: 0, data }) }); log::info!( - target: "runtime::system", + target: LOG_TARGET, "Applied migration from single u8 to triple reference counting to {:?} elements.", translated ); @@ -94,7 +95,7 @@ pub fn migrate_from_single_to_triple_ref_count() -> Weight }, ); log::info!( - target: "runtime::system", + target: LOG_TARGET, "Applied migration from single to triple reference counting to {:?} elements.", translated ); @@ -112,7 +113,7 @@ pub fn migrate_from_dual_to_triple_ref_count() -> Weight { }, ); log::info!( - target: "runtime::system", + target: LOG_TARGET, "Applied migration from dual to triple reference counting to {:?} elements.", translated ); diff --git a/frame/tips/src/lib.rs b/frame/tips/src/lib.rs index dd9ebc9813233..43002c7b5f196 100644 --- a/frame/tips/src/lib.rs +++ b/frame/tips/src/lib.rs @@ -78,6 +78,8 @@ use frame_support::{ pub use pallet::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::tips"; + pub type BalanceOf = pallet_treasury::BalanceOf; pub type NegativeImbalanceOf = pallet_treasury::NegativeImbalanceOf; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; diff --git a/frame/tips/src/migrations/v4.rs b/frame/tips/src/migrations/v4.rs index 5e10fa7dd2c6d..0107e96a41168 100644 --- a/frame/tips/src/migrations/v4.rs +++ b/frame/tips/src/migrations/v4.rs @@ -18,6 +18,7 @@ use sp_io::hashing::twox_128; use sp_std::str; +use super::super::LOG_TARGET; use frame_support::{ storage::StoragePrefixedMap, traits::{ @@ -46,7 +47,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::tips", + target: LOG_TARGET, "Running migration to v4 for tips with storage version {:?}", on_chain_storage_version, ); @@ -80,7 +81,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::tips", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -185,7 +186,7 @@ pub fn post_migrate< fn log_migration(stage: &str, storage_prefix: &[u8], old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::tips", + target: LOG_TARGET, "{} prefix of storage '{}': '{}' ==> '{}'", stage, str::from_utf8(storage_prefix).unwrap_or(""), diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 6b21477be4340..da0f0e0560674 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -60,6 +60,8 @@ pub use pallet::*; pub use types::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::uniques"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] diff --git a/frame/uniques/src/migration.rs b/frame/uniques/src/migration.rs index 8a2a0ef808d90..fd98c030d7f1b 100644 --- a/frame/uniques/src/migration.rs +++ b/frame/uniques/src/migration.rs @@ -24,7 +24,7 @@ pub fn migrate_to_v1, I: 'static, P: GetStorageVersion + PalletInfo ) -> frame_support::weights::Weight { let on_chain_storage_version =

::on_chain_storage_version(); log::info!( - target: "runtime::uniques", + target: LOG_TARGET, "Running migration storage v1 for uniques with storage version {:?}", on_chain_storage_version, ); @@ -37,7 +37,7 @@ pub fn migrate_to_v1, I: 'static, P: GetStorageVersion + PalletInfo } StorageVersion::new(1).put::

(); log::info!( - target: "runtime::uniques", + target: LOG_TARGET, "Running migration storage v1 for uniques with storage version {:?} was complete", on_chain_storage_version, ); @@ -45,7 +45,7 @@ pub fn migrate_to_v1, I: 'static, P: GetStorageVersion + PalletInfo T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1) } else { log::warn!( - target: "runtime::uniques", + target: LOG_TARGET, "Attempted to apply migration to v1 but failed because storage version is {:?}", on_chain_storage_version, ); diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 9e9dff8e6f9ea..dfba046754373 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -19,9 +19,9 @@ use codec::HasCompact; pub use ensure::{ - Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, EnsureFixedPointNumber, - EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, EnsureOpAssign, EnsureSub, - EnsureSubAssign, + ensure_pow, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, + EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, + EnsureOpAssign, EnsureSub, EnsureSubAssign, }; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{ @@ -342,7 +342,7 @@ impl SaturatedConversion for T {} /// The *EnsureOps* family functions follows the same behavior as *CheckedOps* but /// returning an [`ArithmeticError`](crate::ArithmeticError) instead of `None`. mod ensure { - use super::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Zero}; + use super::{checked_pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, Zero}; use crate::{ArithmeticError, FixedPointNumber, FixedPointOperand}; /// Performs addition that returns [`ArithmeticError`] instead of wrapping around on overflow. @@ -511,6 +511,27 @@ mod ensure { } } + /// Raises a value to the power of exp, returning `ArithmeticError` if an overflow occurred. + /// + /// Check [`checked_pow`] for more info about border cases. + /// + /// ``` + /// use sp_arithmetic::{traits::ensure_pow, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// ensure_pow(2u64, 64)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + pub fn ensure_pow( + base: T, + exp: usize, + ) -> Result { + checked_pow(base, exp).ok_or(ArithmeticError::Overflow) + } + impl EnsureAdd for T {} impl EnsureSub for T {} impl EnsureMul for T {} @@ -953,6 +974,15 @@ mod tests { test_ensure(values(), &EnsureDiv::ensure_div, &CheckedDiv::checked_div); } + #[test] + fn ensure_pow_works() { + test_ensure( + values().into_iter().map(|(base, exp)| (base, exp as usize)).collect(), + ensure_pow, + |&a, &b| checked_pow(a, b), + ); + } + #[test] fn ensure_add_assign_works() { test_ensure_assign(values(), &EnsureAddAssign::ensure_add_assign, &EnsureAdd::ensure_add); @@ -974,11 +1004,12 @@ mod tests { } /// Test that the ensured function returns the expected un-ensured value. - fn test_ensure(pairs: Vec<(V, V)>, ensured: E, unensured: P) + fn test_ensure(pairs: Vec<(V, W)>, ensured: E, unensured: P) where V: Ensure + core::fmt::Debug + Copy, - E: Fn(V, V) -> Result, - P: Fn(&V, &V) -> Option, + W: Ensure + core::fmt::Debug + Copy, + E: Fn(V, W) -> Result, + P: Fn(&V, &W) -> Option, { for (a, b) in pairs.into_iter() { match ensured(a, b) { @@ -993,11 +1024,12 @@ mod tests { } /// Test that the ensured function modifies `self` to the expected un-ensured value. - fn test_ensure_assign(pairs: Vec<(V, V)>, ensured: E, unensured: P) + fn test_ensure_assign(pairs: Vec<(V, W)>, ensured: E, unensured: P) where V: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy, - E: Fn(&mut V, V) -> Result<(), ArithmeticError>, - P: Fn(V, V) -> Result + std::panic::RefUnwindSafe, + W: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy, + E: Fn(&mut V, W) -> Result<(), ArithmeticError>, + P: Fn(V, W) -> Result + std::panic::RefUnwindSafe, { for (mut a, b) in pairs.into_iter() { let old_a = a; diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 8978cdb11c0c6..6eb19683b4439 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -32,11 +32,11 @@ use impl_trait_for_tuples::impl_for_tuples; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use sp_application_crypto::AppKey; pub use sp_arithmetic::traits::{ - AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedShl, - CheckedShr, CheckedSub, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, - EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, - EnsureOpAssign, EnsureSub, EnsureSubAssign, IntegerSquareRoot, One, SaturatedConversion, - Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, + checked_pow, ensure_pow, AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedDiv, + CheckedMul, CheckedShl, CheckedShr, CheckedSub, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, + EnsureDivAssign, EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, + EnsureOp, EnsureOpAssign, EnsureSub, EnsureSubAssign, IntegerSquareRoot, One, + SaturatedConversion, Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, }; use sp_core::{self, storage::StateVersion, Hasher, RuntimeDebug, TypeId}; #[doc(hidden)] diff --git a/scripts/ci/gitlab/crate-publishing-pipeline.yml b/scripts/ci/gitlab/crate-publishing-pipeline.yml new file mode 100644 index 0000000000000..9d5303952e6ef --- /dev/null +++ b/scripts/ci/gitlab/crate-publishing-pipeline.yml @@ -0,0 +1 @@ +default: !reference [.crate-publishing-pipeline-definitions, default] diff --git a/scripts/ci/gitlab/default-pipeline.yml b/scripts/ci/gitlab/default-pipeline.yml new file mode 100644 index 0000000000000..19f6c320c3c24 --- /dev/null +++ b/scripts/ci/gitlab/default-pipeline.yml @@ -0,0 +1 @@ +default: !reference [.default-pipeline-definitions, default] diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index cc7451a9fbb22..d1a7514d1707b 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -182,20 +182,6 @@ publish-draft-release: - ./scripts/ci/gitlab/publish_draft_release.sh allow_failure: true -# Ref: https://github.com/paritytech/opstooling/issues/111 -update-node-template: - stage: publish - extends: .kubernetes-env - rules: - - if: $CI_COMMIT_REF_NAME =~ /^polkadot-v[0-9]+\.[0-9]+.*$/ # i.e. polkadot-v1.0.99, polkadot-v2.1rc1 - script: - - git clone --depth=1 --branch="$PIPELINE_SCRIPTS_TAG" https://github.com/paritytech/pipeline-scripts - - ./pipeline-scripts/update_substrate_template.sh - --repo-name "substrate-node-template" - --template-path "bin/node-template" - --github-api-token "$GITHUB_TOKEN" - --polkadot-branch "$CI_COMMIT_REF_NAME" - .publish-crates-template: stage: publish extends: .crates-publishing-template @@ -214,7 +200,7 @@ update-node-template: timeout: 9h # A custom publishing environment is used for us to be able to set up protected secrets # specifically for it - environment: publish-crates + environment: publish-crates script: - rusty-cachier snapshot create - git clone @@ -234,5 +220,6 @@ publish-crates: publish-crates-manual: extends: .publish-crates-template + rules: !reference [.crate-publishing-pipeline, rules] when: manual allow_failure: true