From 183083e196f64753f30782e0a6ecba27443388e5 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Wed, 8 Nov 2023 16:23:25 +0800 Subject: [PATCH 01/15] Refactor old code --- pallet/staking/src/lib.rs | 50 ++++++++++++++++++--------------- pallet/staking/src/migration.rs | 0 pallet/staking/src/tests.rs | 24 ++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 pallet/staking/src/migration.rs diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 3c59069da..aa0ec7f7c 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -28,12 +28,13 @@ //! - KTON: Darwinia's commitment token //! - Deposit: Locking RINGs' ticket -// TODO: nomination upper limit - #![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] #![deny(unused_crate_dependencies)] +/// Pallet migrations. +pub mod migration; + #[cfg(test)] mod mock; #[cfg(test)] @@ -55,7 +56,7 @@ use dc_types::{Balance, Moment}; use frame_support::{pallet_prelude::*, DefaultNoBound, EqNoBound, PalletId, PartialEqNoBound}; use frame_system::{pallet_prelude::*, RawOrigin}; use sp_runtime::{ - traits::{AccountIdConversion, Convert}, + traits::{AccountIdConversion, Convert, One}, Perbill, Perquintill, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; @@ -133,7 +134,7 @@ pub mod pallet { amount: Balance, }, /// Unable to pay the staker's reward. - Unpaied { + Unpaid { staker: T::AccountId, amount: Balance, }, @@ -206,12 +207,18 @@ pub mod pallet { #[pallet::getter(fn nominator_of)] pub type Nominators = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; - /// Collator's reward points. + /// Number of blocks authored by the collator within current session. + #[pallet::storage] + #[pallet::unbounded] + #[pallet::getter(fn authored_block_count)] + pub type AuthoredBlocksCount = + StorageValue<_, (BlockNumberFor, BTreeMap>), ValueQuery>; + + /// All outstanding rewards since the last payment.. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn reward_points)] - pub type RewardPoints = - StorageValue<_, (RewardPoint, BTreeMap), ValueQuery>; + #[pallet::getter(fn unpaid)] + pub type Unpaid = StorageMap<_, Twox64Concat, T::AccountId, Balance>; /// Active session's start-time. #[pallet::storage] @@ -735,13 +742,13 @@ pub mod pallet { }); } - /// Add reward points to collators using their account id. - pub fn reward_by_ids(collators: &[(T::AccountId, RewardPoint)]) { - >::mutate(|(total, reward_map)| { - collators.iter().cloned().for_each(|(c, p)| { - *total += p; + /// Update the record of block production. + pub fn note_authors(collators: &[T::AccountId]) { + >::mutate(|(sum, map)| { + collators.iter().cloned().for_each(|c| { + *sum += One::one(); - reward_map.entry(c).and_modify(|p_| *p_ += p).or_insert(p); + map.entry(c).and_modify(|p_| *p_ += One::one()).or_insert(One::one()); }); }); } @@ -775,11 +782,11 @@ pub mod pallet { // TODO: weight /// Pay the session reward to the stakers. pub fn payout(amount: Balance) -> Balance { - let (total_points, reward_map) = >::get(); + let (sum, map) = >::get(); // Due to the `payout * percent` there might be some losses. let mut actual_payout = 0; - for (c, p) in reward_map { + for (c, p) in map { let Some(commission) = >::get(&c) else { #[cfg(test)] panic!("[pallet::staking] collator({c:?}) must be found; qed"); @@ -790,7 +797,7 @@ pub mod pallet { continue; } }; - let c_total_payout = Perbill::from_rational(p, total_points) * amount; + let c_total_payout = Perbill::from_rational(p, sum) * amount; let mut c_payout = commission * c_total_payout; let n_payout = c_total_payout - c_payout; let Some(c_exposure) = >::get(&c) else { @@ -820,7 +827,7 @@ pub mod pallet { amount: n_payout, }); } else { - Self::deposit_event(Event::Unpaied { + Self::deposit_event(Event::Unpaid { staker: n_exposure.who, amount: n_payout, }); @@ -832,7 +839,7 @@ pub mod pallet { Self::deposit_event(Event::Payout { staker: c, amount: c_payout }); } else { - Self::deposit_event(Event::Unpaied { staker: c, amount: c_payout }); + Self::deposit_event(Event::Unpaid { staker: c, amount: c_payout }); } } @@ -841,7 +848,7 @@ pub mod pallet { /// Prepare the session state. pub fn prepare_new_session() { - >::kill(); + >::kill(); #[allow(deprecated)] >::remove_all(None); >::iter().drain().for_each(|(k, v)| { @@ -891,7 +898,6 @@ pub mod pallet { } pub use pallet::*; -type RewardPoint = u32; type Power = u32; type Vote = u32; @@ -1042,7 +1048,7 @@ where T: Config + pallet_authorship::Config + pallet_session::Config, { fn note_author(author: T::AccountId) { - Self::reward_by_ids(&[(author, 20)]) + Self::note_authors(&[author]) } } diff --git a/pallet/staking/src/migration.rs b/pallet/staking/src/migration.rs new file mode 100644 index 000000000..e69de29bb diff --git a/pallet/staking/src/tests.rs b/pallet/staking/src/tests.rs index e525b6a24..98c90dac9 100644 --- a/pallet/staking/src/tests.rs +++ b/pallet/staking/src/tests.rs @@ -552,7 +552,7 @@ fn payout_should_work() { }); new_session(); new_session(); - Staking::reward_by_ids(&[(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]); + Staking::note_authors(&[1, 2, 3, 4, 5]); (1..=10).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); let session_duration = Duration::new(6 * 60 * 60, 0).as_millis(); @@ -611,7 +611,7 @@ fn payout_should_work() { }); new_session(); new_session(); - Staking::reward_by_ids(&[(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]); + Staking::note_authors(&[1, 2, 3, 4, 5]); (1..=10).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); let total_issuance = Balances::total_issuance(); @@ -647,7 +647,7 @@ fn payout_should_work() { false )); dbg!(Balances::free_balance(&Treasury::account_id())); - Staking::reward_by_ids(&[(1, 20)]); + Staking::note_authors(&[1]); System::reset_events(); new_session(); @@ -655,13 +655,13 @@ fn payout_should_work() { System::events() .into_iter() .filter_map(|e| match e.event { - RuntimeEvent::Staking(e @ Event::Unpaied { .. }) => Some(e), + RuntimeEvent::Staking(e @ Event::Unpaid { .. }) => Some(e), _ => None, }) .collect::>(), vec![ - Event::Unpaied { staker: 6, amount: 7499999997000000000000 }, - Event::Unpaied { staker: 1, amount: 2499999994000000000000 } + Event::Unpaid { staker: 6, amount: 7499999997000000000000 }, + Event::Unpaid { staker: 1, amount: 2499999994000000000000 } ] ); }); @@ -676,9 +676,7 @@ fn on_new_session_should_work() { assert_ok!(Staking::collect(RuntimeOrigin::signed(3), Perbill::zero())); assert_ok!(Staking::stake(RuntimeOrigin::signed(3), 2 * UNIT, 0, Vec::new())); assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), 3)); - Staking::reward_by_ids( - &Session::validators().into_iter().map(|v| (v, 20)).collect::>(), - ); + Staking::note_authors(&Session::validators()); new_session(); assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); @@ -688,9 +686,7 @@ fn on_new_session_should_work() { assert_ok!(Staking::collect(RuntimeOrigin::signed(4), Perbill::zero())); assert_ok!(Staking::stake(RuntimeOrigin::signed(4), 2 * UNIT, 0, Vec::new())); assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), 4)); - Staking::reward_by_ids( - &Session::validators().into_iter().map(|v| (v, 20)).collect::>(), - ); + Staking::note_authors(&Session::validators()); new_session(); assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); @@ -700,9 +696,7 @@ fn on_new_session_should_work() { assert_ok!(Staking::collect(RuntimeOrigin::signed(5), Perbill::zero())); assert_ok!(Staking::stake(RuntimeOrigin::signed(5), 2 * UNIT, 0, Vec::new())); assert_ok!(Staking::nominate(RuntimeOrigin::signed(5), 5)); - Staking::reward_by_ids( - &Session::validators().into_iter().map(|v| (v, 20)).collect::>(), - ); + Staking::note_authors(&Session::validators()); new_session(); assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); From 0802e649ba59efe9f025c0e096227e2d79ea71db Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Mon, 13 Nov 2023 17:40:03 +0800 Subject: [PATCH 02/15] Improvement part.1 --- pallet/account-migration/src/mock.rs | 2 +- pallet/staking/src/lib.rs | 269 +++++++++++------------- pallet/staking/src/migration.rs | 1 + pallet/staking/src/mock.rs | 53 ++--- pallet/staking/src/tests.rs | 2 +- precompile/staking/src/mock.rs | 2 +- runtime/crab/src/pallets/staking.rs | 6 +- runtime/darwinia/src/pallets/staking.rs | 16 +- runtime/pangolin/src/pallets/staking.rs | 6 +- runtime/pangoro/src/pallets/staking.rs | 16 +- 10 files changed, 179 insertions(+), 194 deletions(-) diff --git a/pallet/account-migration/src/mock.rs b/pallet/account-migration/src/mock.rs index 06c4eeac7..f268d6c31 100644 --- a/pallet/account-migration/src/mock.rs +++ b/pallet/account-migration/src/mock.rs @@ -165,7 +165,7 @@ impl darwinia_staking::Config for Runtime { type MaxDeposits = (); type MaxUnstakings = (); type MinStakingDuration = (); - type OnSessionEnd = (); + type InflationManager = (); type Ring = Dummy; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index aa0ec7f7c..f61810c44 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -53,7 +53,9 @@ use codec::FullCodec; // darwinia use dc_types::{Balance, Moment}; // substrate -use frame_support::{pallet_prelude::*, DefaultNoBound, EqNoBound, PalletId, PartialEqNoBound}; +use frame_support::{ + pallet_prelude::*, traits::Currency, DefaultNoBound, EqNoBound, PalletId, PartialEqNoBound, +}; use frame_system::{pallet_prelude::*, RawOrigin}; use sp_runtime::{ traits::{AccountIdConversion, Convert, One}, @@ -90,8 +92,11 @@ pub mod pallet { /// Deposit [`StakeExt`] interface. type Deposit: StakeExt; - /// On session end handler. - type OnSessionEnd: OnSessionEnd; + /// Currency interface to pay the reward. + type Currency: Currency; + + /// Inflation and reward manager. + type InflationManager: InflationManager; /// Minimum time to stake at least. #[pallet::constant] @@ -128,7 +133,7 @@ pub mod pallet { who: T::AccountId, commission: Perbill, }, - /// A payout has been made for the staker. + /// A payout_inner has been made for the staker. Payout { staker: T::AccountId, amount: Balance, @@ -158,6 +163,8 @@ pub mod pallet { TargetNotCollator, /// Collator count mustn't be zero. ZeroCollatorCount, + /// No reward to pay for this collator. + NoReward, } /// All staking ledgers. @@ -182,18 +189,18 @@ pub mod pallet { #[pallet::getter(fn collator_of)] pub type Collators = StorageMap<_, Twox64Concat, T::AccountId, Perbill>; - /// Current stakers' exposure. + /// Previous stakers' exposure. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn exposure_of)] - pub type Exposures = + #[pallet::getter(fn previous_exposure_of)] + pub type PreviousExposures = StorageMap<_, Twox64Concat, T::AccountId, Exposure>; - /// Next stakers' exposure. + /// Active stakers' exposure. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn next_exposure_of)] - pub type NextExposures = + #[pallet::getter(fn exposure_of)] + pub type Exposures = StorageMap<_, Twox64Concat, T::AccountId, Exposure>; /// The ideal number of active collators. @@ -267,6 +274,17 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(PhantomData); + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_: BlockNumberFor) -> Weight { + >::iter_keys().take(1).for_each(|e| { + // TODO: benchmark + let _ = Self::payout_inner(e); + }); + + Default::default() + } + } #[pallet::call] impl Pallet { /// Add stakes to the staking pool. @@ -371,8 +389,6 @@ pub mod pallet { DispatchResult::Ok(()) })?; - // TODO: event? - Ok(()) } @@ -418,8 +434,6 @@ pub mod pallet { DispatchResult::Ok(()) })?; - // TODO: event? - Ok(()) } @@ -433,8 +447,6 @@ pub mod pallet { Self::claim_unstakings(&who)?; Self::try_clean_ledger_of(&who); - // TODO: event? - Ok(()) } @@ -470,8 +482,6 @@ pub mod pallet { >::mutate(&who, |n| *n = Some(target)); - // TODO: event? - Ok(()) } @@ -488,7 +498,17 @@ pub mod pallet { >::remove(&who); >::remove(&who); - // TODO: event? + Ok(()) + } + + // TODO: benchmark + /// Making the payout for the specified collators and its nominators. + #[pallet::call_index(8)] + #[pallet::weight(0)] + pub fn payout(origin: OriginFor, who: T::AccountId) -> DispatchResult { + ensure_signed(origin)?; + + Self::payout_inner(who)?; Ok(()) } @@ -743,9 +763,9 @@ pub mod pallet { } /// Update the record of block production. - pub fn note_authors(collators: &[T::AccountId]) { + pub fn note_authors(authors: &[T::AccountId]) { >::mutate(|(sum, map)| { - collators.iter().cloned().for_each(|c| { + authors.iter().cloned().for_each(|c| { *sum += One::one(); map.entry(c).and_modify(|p_| *p_ += One::one()).or_insert(One::one()); @@ -779,81 +799,87 @@ pub mod pallet { .unwrap_or_default() } - // TODO: weight - /// Pay the session reward to the stakers. - pub fn payout(amount: Balance) -> Balance { - let (sum, map) = >::get(); - // Due to the `payout * percent` there might be some losses. - let mut actual_payout = 0; - - for (c, p) in map { - let Some(commission) = >::get(&c) else { - #[cfg(test)] - panic!("[pallet::staking] collator({c:?}) must be found; qed"); - #[cfg(not(test))] - { - log::error!("[pallet::staking] collator({c:?}) must be found; qed"); - - continue; - } - }; - let c_total_payout = Perbill::from_rational(p, sum) * amount; - let mut c_payout = commission * c_total_payout; - let n_payout = c_total_payout - c_payout; - let Some(c_exposure) = >::get(&c) else { - #[cfg(test)] - panic!("[pallet::staking] exposure({c:?}) must be found; qed"); - #[cfg(not(test))] - { - log::error!("[pallet::staking] exposure({c:?}) must be found; qed"); - - continue; - } - }; - - for n_exposure in c_exposure.nominators { - let n_payout = - Perbill::from_rational(n_exposure.vote, c_exposure.vote) * n_payout; + /// Pay the session reward to staking pot and update the stakers' reward record. + pub fn dispatch_session_reward(amount: Balance) -> Balance { + let (sum, map) = >::take(); + let staking_pot = account_id(); + let mut actual_reward = 0; + let mut unpaid = 0; - if c == n_exposure.who { - // If the collator nominated themselves. + map.into_iter().for_each(|(c, p)| { + let r = Perbill::from_rational(p, sum) * amount; - c_payout += n_payout; - } else if T::OnSessionEnd::reward(&n_exposure.who, n_payout).is_ok() { - actual_payout += n_payout; + >::mutate(&c, |u| u.map(|u| u + r).or(Some(r))); - Self::deposit_event(Event::Payout { - staker: n_exposure.who, - amount: n_payout, - }); - } else { - Self::deposit_event(Event::Unpaid { - staker: n_exposure.who, - amount: n_payout, - }); - } + if T::InflationManager::reward(&staking_pot, r).is_ok() { + actual_reward += r; + } else { + unpaid += r; } + }); + + Self::deposit_event(Event::Payout { + staker: staking_pot.clone(), + amount: actual_reward, + }); + + if unpaid != 0 { + Self::deposit_event(Event::Unpaid { staker: staking_pot, amount: unpaid }); + } - if T::OnSessionEnd::reward(&c, c_payout).is_ok() { - actual_payout += c_payout; + actual_reward + } - Self::deposit_event(Event::Payout { staker: c, amount: c_payout }); + // TODO: benchmark this function + /// Pay the reward to the collator and its nominators. + pub fn payout_inner(collator: T::AccountId) -> DispatchResult { + let c_exposure = >::get(&collator).ok_or(>::NoReward)?; + let c_total_payout = >::get(&collator).ok_or(>::NoReward)?; + let mut c_payout = c_exposure.commission * c_total_payout; + let n_payout = c_total_payout - c_payout; + for n_exposure in c_exposure.nominators { + let n_payout = Perbill::from_rational(n_exposure.vote, c_exposure.vote) * n_payout; + + if collator == n_exposure.who { + // If the collator nominated themselves. + + c_payout += n_payout; + } else if T::InflationManager::reward(&n_exposure.who, n_payout).is_ok() { + Self::deposit_event(Event::Payout { staker: n_exposure.who, amount: n_payout }); } else { - Self::deposit_event(Event::Unpaid { staker: c, amount: c_payout }); + Self::deposit_event(Event::Unpaid { staker: n_exposure.who, amount: n_payout }); } } - actual_payout + if T::InflationManager::reward(&collator, c_payout).is_ok() { + Self::deposit_event(Event::Payout { staker: collator, amount: c_payout }); + } else { + Self::deposit_event(Event::Unpaid { staker: collator, amount: c_payout }); + } + + Ok(()) } /// Prepare the session state. - pub fn prepare_new_session() { - >::kill(); + pub fn prepare_new_session(index: u32) -> Vec { + // Generally, this storage should be empty. #[allow(deprecated)] - >::remove_all(None); - >::iter().drain().for_each(|(k, v)| { - >::insert(k, v); + >::remove_all(None); + >::iter().drain().for_each(|(k, v)| { + >::insert(k, v); }); + + log::info!( + "[pallet::staking] assembling new collators for new session {index} at #{:?}", + >::block_number(), + ); + + let collators = Self::elect(); + + // TODO?: if we really need this event + Self::deposit_event(Event::Elected { collators: collators.clone() }); + + collators } /// Elect the new collators. @@ -878,7 +904,7 @@ pub mod pallet { }) .collect(); - ((c, Exposure { vote: collator_v, nominators }), collator_v) + ((c, Exposure { commission: cm, vote: collator_v, nominators }), collator_v) }) .collect::>(); @@ -888,7 +914,7 @@ pub mod pallet { .into_iter() .take(>::get() as _) .map(|((c, e), _)| { - >::insert(&c, e); + >::insert(&c, e); c }) @@ -903,10 +929,8 @@ type Vote = u32; type DepositId = <::Deposit as Stake>::Item; -/// On session end handler. -/// -/// Currently it is only used to control the inflation. -pub trait OnSessionEnd +/// Inflation and reward manager. +pub trait InflationManager where T: Config, { @@ -914,42 +938,38 @@ where fn on_session_end() { let inflation = Self::inflate(); let reward = Self::calculate_reward(inflation); - let actual_payout = >::payout(reward); + let actual_reward = >::dispatch_session_reward(reward); - Self::clean(inflation.unwrap_or(reward).saturating_sub(actual_payout)); + if inflation != 0 { + Self::clear(inflation.saturating_sub(actual_reward)); + } } /// Inflation settings. - fn inflate() -> Option { - None + fn inflate() -> Balance { + 0 } /// Calculate the reward. - fn calculate_reward(maybe_inflation: Option) -> Balance; + fn calculate_reward(inflation: Balance) -> Balance; /// The reward function. fn reward(who: &T::AccountId, amount: Balance) -> DispatchResult; - /// Clean the data; currently, only the unissued reward is present. - fn clean(_unissued: Balance) {} + /// Clear the remaining inflation. + fn clear(_remaining: Balance) {} } -impl OnSessionEnd for () +impl InflationManager for () where T: Config, { - fn inflate() -> Option { - None - } - - fn calculate_reward(_maybe_inflation: Option) -> Balance { + fn calculate_reward(_inflation: Balance) -> Balance { 0 } fn reward(_who: &T::AccountId, _amount: Balance) -> DispatchResult { Ok(()) } - - fn clean(_unissued: Balance) {} } /// A convertor from collators id. Since this pallet does not have stash/controller, this is @@ -995,42 +1015,17 @@ where } } -// TODO: remove these /// A snapshot of the stake backing a single collator in the system. -#[cfg(feature = "try-runtime")] -#[derive(PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] -pub struct Exposure -where - AccountId: PartialEq, -{ - /// The total vote backing this collator. - pub vote: Vote, - /// Nominator staking map. - pub nominators: Vec>, -} -/// A snapshot of the stake backing a single collator in the system. -#[cfg(not(feature = "try-runtime"))] #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] pub struct Exposure { + /// The commission of this collator. + pub commission: Perbill, /// The total vote backing this collator. pub vote: Vote, /// Nominator staking map. pub nominators: Vec>, } /// A snapshot of the staker's state. -#[cfg(feature = "try-runtime")] -#[derive(PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] -pub struct IndividualExposure -where - AccountId: PartialEq, -{ - /// Nominator. - pub who: AccountId, - /// Nominator's staking vote. - pub vote: Vote, -} -/// A snapshot of the staker's state. -#[cfg(not(feature = "try-runtime"))] #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] pub struct IndividualExposure { /// Nominator. @@ -1058,25 +1053,13 @@ where T: Config, { fn end_session(_: u32) { - T::OnSessionEnd::on_session_end(); + T::InflationManager::on_session_end(); } fn start_session(_: u32) {} fn new_session(index: u32) -> Option> { - Self::prepare_new_session(); - - log::info!( - "[pallet::staking] assembling new collators for new session {} at #{:?}", - index, - >::block_number(), - ); - - let collators = Self::elect(); - - Self::deposit_event(Event::Elected { collators: collators.clone() }); - - Some(collators) + Some(Self::prepare_new_session(index)) } } diff --git a/pallet/staking/src/migration.rs b/pallet/staking/src/migration.rs index e69de29bb..8b1378917 100644 --- a/pallet/staking/src/migration.rs +++ b/pallet/staking/src/migration.rs @@ -0,0 +1 @@ + diff --git a/pallet/staking/src/mock.rs b/pallet/staking/src/mock.rs index b88cf9af0..2422c032b 100644 --- a/pallet/staking/src/mock.rs +++ b/pallet/staking/src/mock.rs @@ -21,7 +21,7 @@ pub use crate as darwinia_staking; // darwinia use dc_types::{AssetId, Balance, Moment, UNIT}; // substrate -use frame_support::traits::{Currency, OnInitialize, OnUnbalanced}; +use frame_support::traits::{Currency, OnInitialize}; use sp_io::TestExternalities; use sp_runtime::{BuildStorage, RuntimeAppPublic}; @@ -232,7 +232,7 @@ impl pallet_treasury::Config for Runtime { frame_support::parameter_types! { pub PayoutFraction: sp_runtime::Perbill = sp_runtime::Perbill::from_percent(40); - pub static OnSessionEnd: u8 = 0; + pub static InflationType: u8 = 0; } pub enum KtonStaking {} impl darwinia_staking::Stake for KtonStaking { @@ -258,42 +258,42 @@ impl darwinia_staking::Stake for KtonStaking { } } pub enum StatedOnSessionEnd {} -impl darwinia_staking::OnSessionEnd for StatedOnSessionEnd { - fn inflate() -> Option { - if ON_SESSION_END.with(|v| *v.borrow()) == 0 { +impl darwinia_staking::InflationManager for StatedOnSessionEnd { + fn inflate() -> Balance { + if INFLATION_TYPE.with(|v| *v.borrow()) == 0 { OnDarwiniaSessionEnd::inflate() } else { OnCrabSessionEnd::inflate() } } - fn calculate_reward(maybe_inflation: Option) -> Balance { - if ON_SESSION_END.with(|v| *v.borrow()) == 0 { - OnDarwiniaSessionEnd::calculate_reward(maybe_inflation) + fn calculate_reward(inflation: Balance) -> Balance { + if INFLATION_TYPE.with(|v| *v.borrow()) == 0 { + OnDarwiniaSessionEnd::calculate_reward(inflation) } else { - OnCrabSessionEnd::calculate_reward(maybe_inflation) + OnCrabSessionEnd::calculate_reward(inflation) } } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { - if ON_SESSION_END.with(|v| *v.borrow()) == 0 { + if INFLATION_TYPE.with(|v| *v.borrow()) == 0 { OnDarwiniaSessionEnd::reward(who, amount) } else { OnCrabSessionEnd::reward(who, amount) } } - fn clean(unissued: Balance) { - if ON_SESSION_END.with(|v| *v.borrow()) == 0 { - OnDarwiniaSessionEnd::clean(unissued) + fn clear(remaining: Balance) { + if INFLATION_TYPE.with(|v| *v.borrow()) == 0 { + OnDarwiniaSessionEnd::clear(remaining) } else { - OnCrabSessionEnd::clean(unissued) + OnCrabSessionEnd::clear(remaining) } } } pub enum OnDarwiniaSessionEnd {} -impl darwinia_staking::OnSessionEnd for OnDarwiniaSessionEnd { - fn inflate() -> Option { +impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { + fn inflate() -> Balance { let now = Timestamp::now(); let session_duration = now - >::get(); let elapsed_time = >::mutate(|t| { @@ -306,11 +306,11 @@ impl darwinia_staking::OnSessionEnd for OnDarwiniaSessionEnd { let unminted = dc_inflation::TOTAL_SUPPLY.saturating_sub(Balances::total_issuance()); - dc_inflation::in_period(unminted, session_duration, elapsed_time) + dc_inflation::in_period(unminted, session_duration, elapsed_time).unwrap_or_default() } - fn calculate_reward(maybe_inflation: Option) -> Balance { - maybe_inflation.map(|i| PayoutFraction::get() * i).unwrap_or_default() + fn calculate_reward(inflation: Balance) -> Balance { + PayoutFraction::get() * inflation } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { @@ -319,13 +319,13 @@ impl darwinia_staking::OnSessionEnd for OnDarwiniaSessionEnd { Ok(()) } - fn clean(unissued: Balance) { - Treasury::on_unbalanced(Balances::issue(unissued)); + fn clear(remaining: Balance) { + let _ = Balances::deposit_into_existing(&Treasury::account_id(), remaining); } } pub enum OnCrabSessionEnd {} -impl darwinia_staking::OnSessionEnd for OnCrabSessionEnd { - fn calculate_reward(_maybe_inflation: Option) -> Balance { +impl darwinia_staking::InflationManager for OnCrabSessionEnd { + fn calculate_reward(_inflation: Balance) -> Balance { 10_000 * UNIT } @@ -339,12 +339,13 @@ impl darwinia_staking::OnSessionEnd for OnCrabSessionEnd { } } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = StatedOnSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = frame_support::traits::ConstU32<16>; type MinStakingDuration = frame_support::traits::ConstU64<3>; - type OnSessionEnd = StatedOnSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); @@ -399,8 +400,8 @@ pub struct ExtBuilder { genesis_collator: bool, } impl ExtBuilder { - pub fn on_session_end_type(self, r#type: u8) -> Self { - ON_SESSION_END.with(|v| *v.borrow_mut() = r#type); + pub fn inflation_type(self, r#type: u8) -> Self { + INFLATION_TYPE.with(|v| *v.borrow_mut() = r#type); self } diff --git a/pallet/staking/src/tests.rs b/pallet/staking/src/tests.rs index 98c90dac9..e54c2da27 100644 --- a/pallet/staking/src/tests.rs +++ b/pallet/staking/src/tests.rs @@ -589,7 +589,7 @@ fn payout_should_work() { ); }); - ExtBuilder::default().on_session_end_type(1).collator_count(5).build().execute_with(|| { + ExtBuilder::default().inflation_type(1).collator_count(5).build().execute_with(|| { (1..=5).for_each(|i| { assert_ok!(Staking::stake( RuntimeOrigin::signed(i), diff --git a/precompile/staking/src/mock.rs b/precompile/staking/src/mock.rs index 2e27ad8d7..e3660f4d4 100644 --- a/precompile/staking/src/mock.rs +++ b/precompile/staking/src/mock.rs @@ -237,7 +237,7 @@ impl darwinia_staking::Config for Runtime { type MaxDeposits = ::MaxDeposits; type MaxUnstakings = frame_support::traits::ConstU32<16>; type MinStakingDuration = frame_support::traits::ConstU64<3>; - type OnSessionEnd = (); + type InflationManager = (); type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/runtime/crab/src/pallets/staking.rs b/runtime/crab/src/pallets/staking.rs index 7fafe2ffc..e6e7ec88c 100644 --- a/runtime/crab/src/pallets/staking.rs +++ b/runtime/crab/src/pallets/staking.rs @@ -73,8 +73,8 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnCrabSessionEnd {} -impl darwinia_staking::OnSessionEnd for OnCrabSessionEnd { - fn calculate_reward(_maybe_inflation: Option) -> Balance { +impl darwinia_staking::InflationManager for OnCrabSessionEnd { + fn calculate_reward(_inflation: Balance) -> Balance { 20_000 * UNIT } @@ -94,7 +94,7 @@ impl darwinia_staking::Config for Runtime { type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = MinStakingDuration; - type OnSessionEnd = OnCrabSessionEnd; + type InflationManager = OnCrabSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; diff --git a/runtime/darwinia/src/pallets/staking.rs b/runtime/darwinia/src/pallets/staking.rs index 9e2398355..f0ef5ca81 100644 --- a/runtime/darwinia/src/pallets/staking.rs +++ b/runtime/darwinia/src/pallets/staking.rs @@ -73,8 +73,8 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnDarwiniaSessionEnd {} -impl darwinia_staking::OnSessionEnd for OnDarwiniaSessionEnd { - fn inflate() -> Option { +impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { + fn inflate() -> Balance { let now = Timestamp::now() as Moment; let session_duration = now - >::get(); let elapsed_time = >::mutate(|t| { @@ -87,11 +87,11 @@ impl darwinia_staking::OnSessionEnd for OnDarwiniaSessionEnd { let unminted = dc_inflation::TOTAL_SUPPLY.saturating_sub(Balances::total_issuance()); - dc_inflation::in_period(unminted, session_duration, elapsed_time) + dc_inflation::in_period(unminted, session_duration, elapsed_time).unwrap_or_default() } - fn calculate_reward(maybe_inflation: Option) -> Balance { - maybe_inflation.map(|i| sp_runtime::Perbill::from_percent(40) * i).unwrap_or_default() + fn calculate_reward(inflation: Balance) -> Balance { + sp_runtime::Perbill::from_percent(40) * inflation } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { @@ -100,8 +100,8 @@ impl darwinia_staking::OnSessionEnd for OnDarwiniaSessionEnd { Ok(()) } - fn clean(unissued: Balance) { - Treasury::on_unbalanced(Balances::issue(unissued)); + fn clear(remaining: Balance) { + let _ = Balances::deposit_into_existing(&Treasury::account_id(), remaining); } } @@ -111,7 +111,7 @@ impl darwinia_staking::Config for Runtime { type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = MinStakingDuration; - type OnSessionEnd = OnDarwiniaSessionEnd; + type InflationManager = OnDarwiniaSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; diff --git a/runtime/pangolin/src/pallets/staking.rs b/runtime/pangolin/src/pallets/staking.rs index fe78c2833..29504f24c 100644 --- a/runtime/pangolin/src/pallets/staking.rs +++ b/runtime/pangolin/src/pallets/staking.rs @@ -67,8 +67,8 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnPangolinSessionEnd {} -impl darwinia_staking::OnSessionEnd for OnPangolinSessionEnd { - fn calculate_reward(_maybe_inflation: Option) -> Balance { +impl darwinia_staking::InflationManager for OnPangolinSessionEnd { + fn calculate_reward(_inflation: Balance) -> Balance { 20_000 * UNIT } @@ -88,7 +88,7 @@ impl darwinia_staking::Config for Runtime { type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = ConstU32<{ 2 * MINUTES }>; - type OnSessionEnd = OnPangolinSessionEnd; + type InflationManager = OnPangolinSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; diff --git a/runtime/pangoro/src/pallets/staking.rs b/runtime/pangoro/src/pallets/staking.rs index 8deddfaa9..20f25ae01 100644 --- a/runtime/pangoro/src/pallets/staking.rs +++ b/runtime/pangoro/src/pallets/staking.rs @@ -69,8 +69,8 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnPangoroSessionEnd {} -impl darwinia_staking::OnSessionEnd for OnPangoroSessionEnd { - fn inflate() -> Option { +impl darwinia_staking::InflationManager for OnPangoroSessionEnd { + fn inflate() -> Balance { let now = Timestamp::now() as Moment; let session_duration = now - >::get() as Moment; let elapsed_time = >::mutate(|t| { @@ -83,11 +83,11 @@ impl darwinia_staking::OnSessionEnd for OnPangoroSessionEnd { let unminted = dc_inflation::TOTAL_SUPPLY.saturating_sub(Balances::total_issuance()); - dc_inflation::in_period(unminted, session_duration, elapsed_time) + dc_inflation::in_period(unminted, session_duration, elapsed_time).unwrap_or_default() } - fn calculate_reward(maybe_inflation: Option) -> Balance { - maybe_inflation.map(|i| sp_runtime::Perbill::from_percent(40) * i).unwrap_or_default() + fn calculate_reward(inflation: Balance) -> Balance { + sp_runtime::Perbill::from_percent(40) * inflation } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { @@ -96,8 +96,8 @@ impl darwinia_staking::OnSessionEnd for OnPangoroSessionEnd { Ok(()) } - fn clean(unissued: Balance) { - Treasury::on_unbalanced(Balances::issue(unissued)); + fn clear(remaining: Balance) { + let _ = Balances::deposit_into_existing(&Treasury::account_id(), remaining); } } @@ -107,7 +107,7 @@ impl darwinia_staking::Config for Runtime { type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = ConstU32<{ 10 * MINUTES }>; - type OnSessionEnd = OnPangoroSessionEnd; + type InflationManager = OnPangoroSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; From 99b709de8c8d7e78c836a56dc98a033e1f022838 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 02:30:34 +0800 Subject: [PATCH 03/15] Improvement part.2 --- pallet/staking/src/lib.rs | 4 +- pallet/staking/src/mock.rs | 10 ++- pallet/staking/src/tests.rs | 144 ++++++++++++++++++------------------ 3 files changed, 83 insertions(+), 75 deletions(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index f61810c44..a2b05f186 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -809,7 +809,7 @@ pub mod pallet { map.into_iter().for_each(|(c, p)| { let r = Perbill::from_rational(p, sum) * amount; - >::mutate(&c, |u| u.map(|u| u + r).or(Some(r))); + >::mutate(&c, |u| *u = u.map(|u| u + r).or(Some(r))); if T::InflationManager::reward(&staking_pot, r).is_ok() { actual_reward += r; @@ -834,7 +834,7 @@ pub mod pallet { /// Pay the reward to the collator and its nominators. pub fn payout_inner(collator: T::AccountId) -> DispatchResult { let c_exposure = >::get(&collator).ok_or(>::NoReward)?; - let c_total_payout = >::get(&collator).ok_or(>::NoReward)?; + let c_total_payout = >::take(&collator).ok_or(>::NoReward)?; let mut c_payout = c_exposure.commission * c_total_payout; let n_payout = c_total_payout - c_payout; for n_exposure in c_exposure.nominators { diff --git a/pallet/staking/src/mock.rs b/pallet/staking/src/mock.rs index 2422c032b..a66b2c81a 100644 --- a/pallet/staking/src/mock.rs +++ b/pallet/staking/src/mock.rs @@ -168,7 +168,7 @@ sp_runtime::impl_opaque_keys! { pub uint: SessionHandler, } } -type Period = frame_support::traits::ConstU64<3>; +pub type Period = frame_support::traits::ConstU64<3>; pub struct SessionHandler; impl pallet_session::SessionHandler for SessionHandler { const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = @@ -461,7 +461,7 @@ impl ExtBuilder { let mut ext = TestExternalities::from(storage); - ext.execute_with(|| initialize_block(1)); + ext.execute_with(|| new_session()); ext } @@ -491,3 +491,9 @@ pub fn new_session() { finalize_block(i); }); } + +pub fn payout() { + >::iter_keys().for_each(|c| { + let _ = Staking::payout_inner(c); + }); +} diff --git a/pallet/staking/src/tests.rs b/pallet/staking/src/tests.rs index e54c2da27..690f525ce 100644 --- a/pallet/staking/src/tests.rs +++ b/pallet/staking/src/tests.rs @@ -119,7 +119,7 @@ fn unstake_should_work() { staked_ring: 2 * UNIT, staked_kton: 3 * UNIT, staked_deposits: BoundedVec::truncate_from(vec![0, 1, 2]), - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6)]), ..ZeroDefault::default() } ); @@ -133,8 +133,8 @@ fn unstake_should_work() { staked_ring: 2 * UNIT, staked_kton: 2 * UNIT, staked_deposits: BoundedVec::truncate_from(vec![0, 1, 2]), - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 5)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7)]), ..ZeroDefault::default() } ); @@ -154,9 +154,9 @@ fn unstake_should_work() { staked_ring: 2 * UNIT, staked_kton: 2 * UNIT, staked_deposits: BoundedVec::truncate_from(vec![0, 2]), - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 5)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(1, 6)]) + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(1, 8)]) } ); @@ -166,9 +166,9 @@ fn unstake_should_work() { assert_eq!( Staking::ledger_of(1).unwrap(), Ledger { - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4), (2 * UNIT, 7)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 5), (2 * UNIT, 7)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(1, 6), (0, 7), (2, 7)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6), (2 * UNIT, 9)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7), (2 * UNIT, 9)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(1, 8), (0, 9), (2, 9)]), ..ZeroDefault::default() } ); @@ -196,9 +196,9 @@ fn restake_should_work() { assert_eq!( Staking::ledger_of(1).unwrap(), Ledger { - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT, 5), (UNIT, 6)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT, 5), (UNIT, 6)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 4), (1, 4), (2, 4)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT, 7), (UNIT, 8)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT, 7), (UNIT, 8)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (1, 6), (2, 6)]), ..ZeroDefault::default() } ); @@ -209,9 +209,9 @@ fn restake_should_work() { Staking::ledger_of(1).unwrap(), Ledger { staked_ring: 3 * UNIT / 2, - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT / 2, 5)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT, 5), (UNIT, 6)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 4), (1, 4), (2, 4)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT / 2, 7)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT, 7), (UNIT, 8)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (1, 6), (2, 6)]), ..ZeroDefault::default() } ); @@ -223,9 +223,9 @@ fn restake_should_work() { Ledger { staked_ring: 3 * UNIT / 2, staked_kton: 3 * UNIT / 2, - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT / 2, 5)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT / 2, 5)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 4), (1, 4), (2, 4)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT / 2, 7)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT / 2, 7)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (1, 6), (2, 6)]), ..ZeroDefault::default() } ); @@ -244,9 +244,9 @@ fn restake_should_work() { staked_ring: 3 * UNIT / 2, staked_kton: 3 * UNIT / 2, staked_deposits: BoundedVec::truncate_from(vec![1]), - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT / 2, 5)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT / 2, 5)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 4), (2, 4)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT / 2, 7)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT / 2, 7)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (2, 6)]), } ); @@ -290,9 +290,9 @@ fn claim_should_work() { assert_eq!( Staking::ledger_of(1).unwrap(), Ledger { - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 4), (UNIT, 7)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 5), (UNIT, 7)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (1, 7), (2, 7)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 6), (UNIT, 9)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7), (UNIT, 9)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 8), (1, 9), (2, 9)]), ..ZeroDefault::default() } ); @@ -304,9 +304,9 @@ fn claim_should_work() { assert_eq!( Staking::ledger_of(1).unwrap(), Ledger { - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 7)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 5), (UNIT, 7)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (1, 7), (2, 7)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 9)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7), (UNIT, 9)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 8), (1, 9), (2, 9)]), ..ZeroDefault::default() } ); @@ -319,9 +319,9 @@ fn claim_should_work() { assert_eq!( Staking::ledger_of(1).unwrap(), Ledger { - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 7)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(0, 6), (1, 7), (2, 7)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 9)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 9)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(0, 8), (1, 9), (2, 9)]), ..ZeroDefault::default() } ); @@ -334,9 +334,9 @@ fn claim_should_work() { assert_eq!( Staking::ledger_of(1).unwrap(), Ledger { - unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 7)]), - unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 7)]), - unstaking_deposits: BoundedVec::truncate_from(vec![(1, 7), (2, 7)]), + unstaking_ring: BoundedVec::truncate_from(vec![(UNIT, 9)]), + unstaking_kton: BoundedVec::truncate_from(vec![(UNIT, 9)]), + unstaking_deposits: BoundedVec::truncate_from(vec![(1, 9), (2, 9)]), ..ZeroDefault::default() } ); @@ -551,36 +551,37 @@ fn payout_should_work() { assert_ok!(Staking::nominate(RuntimeOrigin::signed(i), i - 5)); }); new_session(); - new_session(); - Staking::note_authors(&[1, 2, 3, 4, 5]); (1..=10).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); - let session_duration = Duration::new(6 * 60 * 60, 0).as_millis(); - - Timestamp::set_timestamp(session_duration); + let total_issuance = Balances::total_issuance(); + let session_duration = Duration::new(12 * 600, 0).as_millis(); + Efflux::time(session_duration - >::get() as Moment); + Staking::note_authors(&[1, 2, 3, 4, 5]); new_session(); - - let rewards = [ - 1365981985468705603914, - 2549833009234996736972, - 3551553170742362179958, - 4371142361805028424107, - 5008600632691132655310, - 4097945964602008744224, - 2914094945753252770656, - 1912374784245887327670, - 1092785589904864310528, - 455327319565152874824, + payout(); + + let rewards = vec![ + 455327412809459795649, + 849944493808794062101, + 1183851276145838531437, + 1457047723758662022603, + 1669533853403313367198, + 1365982241160343870361, + 971365161800188293957, + 637458379463143824620, + 364261930757534540090, + 151775801295014161055, ]; - (1..=10) - .zip(rewards.iter()) - .for_each(|(i, r)| assert_eq!(Balances::free_balance(i) - 1_000 * UNIT, *r)); + assert_eq!( + rewards, + (1..=10).map(|i| Balances::free_balance(i) - 1_000 * UNIT).collect::>() + ); assert_eq_error_rate!( PayoutFraction::get() * dc_inflation::in_period( - dc_inflation::TOTAL_SUPPLY - Balances::total_issuance(), + dc_inflation::TOTAL_SUPPLY - total_issuance, session_duration, - 0 + Timestamp::now() ) .unwrap(), rewards.iter().sum::(), @@ -610,15 +611,16 @@ fn payout_should_work() { assert_ok!(Staking::nominate(RuntimeOrigin::signed(i), i - 5)); }); new_session(); - new_session(); - Staking::note_authors(&[1, 2, 3, 4, 5]); (1..=10).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); let total_issuance = Balances::total_issuance(); - + let session_duration = Duration::new(12 * 600, 0).as_millis(); + Efflux::time(session_duration - >::get() as Moment); + Staking::note_authors(&[1, 2, 3, 4, 5]); new_session(); + payout(); - let rewards = [ + let rewards = vec![ 499999998800000000000, 933333320000000000000, 1300000000000000000000, @@ -630,9 +632,10 @@ fn payout_should_work() { 399999999600000000000, 166666663000000000000, ]; - (1..=10) - .zip(rewards.iter()) - .for_each(|(i, r)| assert_eq!(Balances::free_balance(i) - 1_000 * UNIT, *r)); + assert_eq!( + rewards, + (1..=10).map(|i| Balances::free_balance(i) - 1_000 * UNIT).collect::>() + ); assert_eq!(Balances::total_issuance(), total_issuance); assert_eq!( @@ -640,16 +643,15 @@ fn payout_should_work() { 1_000_000 * UNIT - rewards.iter().sum::() ); - dbg!(Balances::free_balance(&Treasury::account_id())); assert_ok!(Balances::transfer_all( RuntimeOrigin::signed(Treasury::account_id()), Default::default(), false )); - dbg!(Balances::free_balance(&Treasury::account_id())); Staking::note_authors(&[1]); System::reset_events(); new_session(); + payout(); assert_eq!( System::events() @@ -670,8 +672,8 @@ fn payout_should_work() { #[test] fn on_new_session_should_work() { ExtBuilder::default().collator_count(2).genesis_collator().build().execute_with(|| { + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); assert_ok!(Staking::collect(RuntimeOrigin::signed(3), Perbill::zero())); assert_ok!(Staking::stake(RuntimeOrigin::signed(3), 2 * UNIT, 0, Vec::new())); @@ -679,8 +681,8 @@ fn on_new_session_should_work() { Staking::note_authors(&Session::validators()); new_session(); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); assert_ok!(Staking::chill(RuntimeOrigin::signed(3))); assert_ok!(Staking::collect(RuntimeOrigin::signed(4), Perbill::zero())); @@ -689,8 +691,8 @@ fn on_new_session_should_work() { Staking::note_authors(&Session::validators()); new_session(); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); assert_ok!(Staking::chill(RuntimeOrigin::signed(4))); assert_ok!(Staking::collect(RuntimeOrigin::signed(5), Perbill::zero())); @@ -699,7 +701,7 @@ fn on_new_session_should_work() { Staking::note_authors(&Session::validators()); new_session(); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 5]); + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); + assert_eq_uvec!(>::iter_keys().collect::>(), [1, 5]); }); } From e202f19fd93e636c3e92892ba49b57eb97621890 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 02:38:09 +0800 Subject: [PATCH 04/15] Improvement part.3 --- pallet/account-migration/src/mock.rs | 3 ++- precompile/staking/src/mock.rs | 3 ++- runtime/crab/src/pallets/staking.rs | 3 ++- runtime/darwinia/src/pallets/staking.rs | 5 +++-- runtime/pangolin/src/pallets/staking.rs | 3 ++- runtime/pangoro/src/pallets/staking.rs | 5 +++-- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/pallet/account-migration/src/mock.rs b/pallet/account-migration/src/mock.rs index f268d6c31..8c52dc9e1 100644 --- a/pallet/account-migration/src/mock.rs +++ b/pallet/account-migration/src/mock.rs @@ -160,12 +160,13 @@ impl darwinia_deposit::Config for Runtime { } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = (); type Kton = Dummy; type MaxDeposits = (); type MaxUnstakings = (); type MinStakingDuration = (); - type InflationManager = (); type Ring = Dummy; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/precompile/staking/src/mock.rs b/precompile/staking/src/mock.rs index e3660f4d4..f2ba7a593 100644 --- a/precompile/staking/src/mock.rs +++ b/precompile/staking/src/mock.rs @@ -232,12 +232,13 @@ impl darwinia_staking::Stake for KtonStaking { } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = (); type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = frame_support::traits::ConstU32<16>; type MinStakingDuration = frame_support::traits::ConstU64<3>; - type InflationManager = (); type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/runtime/crab/src/pallets/staking.rs b/runtime/crab/src/pallets/staking.rs index e6e7ec88c..1421998b3 100644 --- a/runtime/crab/src/pallets/staking.rs +++ b/runtime/crab/src/pallets/staking.rs @@ -89,12 +89,13 @@ impl darwinia_staking::InflationManager for OnCrabSessionEnd { } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = OnCrabSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = MinStakingDuration; - type InflationManager = OnCrabSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; diff --git a/runtime/darwinia/src/pallets/staking.rs b/runtime/darwinia/src/pallets/staking.rs index f0ef5ca81..2a8e76858 100644 --- a/runtime/darwinia/src/pallets/staking.rs +++ b/runtime/darwinia/src/pallets/staking.rs @@ -19,7 +19,7 @@ // darwinia use crate::*; // substrate -use frame_support::traits::{Currency, OnUnbalanced}; +use frame_support::traits::Currency; fast_runtime_or_not!(DURATION, BlockNumber, 5 * MINUTES, 14 * DAYS); @@ -106,12 +106,13 @@ impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = OnDarwiniaSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = MinStakingDuration; - type InflationManager = OnDarwiniaSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; diff --git a/runtime/pangolin/src/pallets/staking.rs b/runtime/pangolin/src/pallets/staking.rs index 29504f24c..fa94b9936 100644 --- a/runtime/pangolin/src/pallets/staking.rs +++ b/runtime/pangolin/src/pallets/staking.rs @@ -83,12 +83,13 @@ impl darwinia_staking::InflationManager for OnPangolinSessionEnd { } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = OnPangolinSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = ConstU32<{ 2 * MINUTES }>; - type InflationManager = OnPangolinSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; diff --git a/runtime/pangoro/src/pallets/staking.rs b/runtime/pangoro/src/pallets/staking.rs index 20f25ae01..5e760aa27 100644 --- a/runtime/pangoro/src/pallets/staking.rs +++ b/runtime/pangoro/src/pallets/staking.rs @@ -19,7 +19,7 @@ // darwinia use crate::*; // substrate -use frame_support::traits::{Currency, OnUnbalanced}; +use frame_support::traits::Currency; pub enum RingStaking {} impl darwinia_staking::Stake for RingStaking { @@ -102,12 +102,13 @@ impl darwinia_staking::InflationManager for OnPangoroSessionEnd { } impl darwinia_staking::Config for Runtime { + type Currency = Balances; type Deposit = Deposit; + type InflationManager = OnPangoroSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; type MinStakingDuration = ConstU32<{ 10 * MINUTES }>; - type InflationManager = OnPangoroSessionEnd; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::darwinia_staking::WeightInfo; From 19d7b9774e5f2ea9f9b314c4bd4003cbeeb10b18 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 03:26:24 +0800 Subject: [PATCH 05/15] Improvement part.4 --- pallet/staking/Cargo.toml | 2 +- pallet/staking/src/benchmarking.rs | 24 +++++++++ pallet/staking/src/lib.rs | 83 ++++++++++++++++++++---------- pallet/staking/src/tests.rs | 60 ++++++++++----------- pallet/staking/src/weights.rs | 7 +++ 5 files changed, 118 insertions(+), 58 deletions(-) diff --git a/pallet/staking/Cargo.toml b/pallet/staking/Cargo.toml index 76d51aec2..7f3b62e4d 100644 --- a/pallet/staking/Cargo.toml +++ b/pallet/staking/Cargo.toml @@ -37,7 +37,7 @@ darwinia-deposit = { workspace = true, features = ["std"] } # substrate dc-inflation = { workspace = true, features = ["std"] } -pallet-assets = { workspace = true, features = ["std"] } +pallet-assets = { workspace = true, features = ["runtime-benchmarks", "std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-session = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true, features = ["std"] } diff --git a/pallet/staking/src/benchmarking.rs b/pallet/staking/src/benchmarking.rs index 342e4f6c2..b9789daa2 100644 --- a/pallet/staking/src/benchmarking.rs +++ b/pallet/staking/src/benchmarking.rs @@ -198,6 +198,30 @@ mod benchmarks { _(RawOrigin::Signed(a)); } + #[benchmark] + fn payout() { + let a = frame_benchmarking::whitelisted_caller::(); + let sender = a.clone(); + + >::insert( + &a, + Exposure { + commission: Perbill::zero(), + vote: 32, + nominators: (0..32) + .map(|i| IndividualExposure { + who: frame_benchmarking::account("", i, i), + vote: 1, + }) + .collect(), + }, + ); + >::insert(&a, 500); + + #[extrinsic_call] + _(RawOrigin::Signed(sender), a); + } + #[benchmark] fn set_collator_count() { // Worst-case scenario: diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index a2b05f186..fd10b2152 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -58,7 +58,7 @@ use frame_support::{ }; use frame_system::{pallet_prelude::*, RawOrigin}; use sp_runtime::{ - traits::{AccountIdConversion, Convert, One}, + traits::{AccountIdConversion, Convert, One, Zero}, Perbill, Perquintill, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; @@ -68,6 +68,9 @@ pub mod pallet { // darwinia use crate::*; + // TODO: limit the number of nominators that a collator can have. + // const MAX_NOMINATIONS: u32 = 32; + // Deposit helper for runtime benchmark. #[cfg(feature = "runtime-benchmarks")] use darwinia_deposit::Config as DepositConfig; @@ -277,12 +280,9 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_: BlockNumberFor) -> Weight { - >::iter_keys().take(1).for_each(|e| { - // TODO: benchmark - let _ = Self::payout_inner(e); - }); - - Default::default() + >::iter_keys() + .take(1) + .fold(Zero::zero(), |acc, e| acc + Self::payout_inner(e).unwrap_or(Zero::zero())) } } #[pallet::call] @@ -501,10 +501,9 @@ pub mod pallet { Ok(()) } - // TODO: benchmark /// Making the payout for the specified collators and its nominators. #[pallet::call_index(8)] - #[pallet::weight(0)] + #[pallet::weight(::WeightInfo::payout())] pub fn payout(origin: OriginFor, who: T::AccountId) -> DispatchResult { ensure_signed(origin)?; @@ -773,28 +772,55 @@ pub mod pallet { }); } - // Power is a mixture of RING and KTON. - // - `total_ring_power = (amount / total_staked_ring) * HALF_POWER` - // - `total_kton_power = (amount / total_staked_kton) * HALF_POWER` - fn balance2power

(amount: Balance) -> Power - where - P: frame_support::StorageValue, - { - (Perquintill::from_rational(amount, P::get().max(1)) * 500_000_000_u128) as _ + /// Calculate the power of the given account. + #[cfg(any(feature = "runtime-benchmarks", test))] + pub fn quick_power_of(who: &T::AccountId) -> Power { + // Power is a mixture of RING and KTON. + // - `total_ring_power = (amount / total_staked_ring) * HALF_POWER` + // - `total_kton_power = (amount / total_staked_kton) * HALF_POWER` + fn balance2power

(amount: Balance) -> Power + where + P: frame_support::StorageValue, + { + (Perquintill::from_rational(amount, P::get().max(1)) * 500_000_000_u128) as _ + } + + >::get(who) + .map(|l| { + balance2power::>( + l.staked_ring + + l.staked_deposits + .into_iter() + // We don't care if the deposit exists here. + // It was guaranteed by the `stake`/`unstake`/`restake` functions. + .fold(0, |r, d| r + T::Deposit::amount(who, d).unwrap_or_default()), + ) + balance2power::>(l.staked_kton) + }) + .unwrap_or_default() } /// Calculate the power of the given account. - pub fn power_of(who: &T::AccountId) -> Power { + /// + /// This is an optimized version of [`Self::quick_power_of`]. + /// Avoiding read the pools' storage multiple times. + pub fn power_of(who: &T::AccountId, ring_pool: Balance, kton_pool: Balance) -> Power { + const HALF_POWER: u128 = 500_000_000; + >::get(who) .map(|l| { - Self::balance2power::>( + (Perquintill::from_rational( l.staked_ring + l.staked_deposits .into_iter() // We don't care if the deposit exists here. // It was guaranteed by the `stake`/`unstake`/`restake` functions. .fold(0, |r, d| r + T::Deposit::amount(who, d).unwrap_or_default()), - ) + Self::balance2power::>(l.staked_kton) + ring_pool.max(1), + ) * HALF_POWER) + .saturating_add( + Perquintill::from_rational(l.staked_kton, kton_pool.max(1)) + * HALF_POWER, + ) as _ }) .unwrap_or_default() } @@ -830,9 +856,8 @@ pub mod pallet { actual_reward } - // TODO: benchmark this function /// Pay the reward to the collator and its nominators. - pub fn payout_inner(collator: T::AccountId) -> DispatchResult { + pub fn payout_inner(collator: T::AccountId) -> Result { let c_exposure = >::get(&collator).ok_or(>::NoReward)?; let c_total_payout = >::take(&collator).ok_or(>::NoReward)?; let mut c_payout = c_exposure.commission * c_total_payout; @@ -857,7 +882,7 @@ pub mod pallet { Self::deposit_event(Event::Unpaid { staker: collator, amount: c_payout }); } - Ok(()) + Ok(::WeightInfo::payout()) } /// Prepare the session state. @@ -886,18 +911,22 @@ pub mod pallet { /// /// This should only be called by the [`pallet_session::SessionManager::new_session`]. pub fn elect() -> Vec { + let nominators = >::iter().collect::>(); + let ring_pool = >::get(); + let kton_pool = >::get(); let mut collators = >::iter() .map(|(c, cm)| { let scaler = Perbill::one() - cm; let mut collator_v = 0; - let nominators = >::iter() + let nominators = nominators + .iter() .filter_map(|(n, c_)| { - if c_ == c { - let nominator_v = scaler * Self::power_of(&n); + if c_ == &c { + let nominator_v = scaler * Self::power_of(n, ring_pool, kton_pool); collator_v += nominator_v; - Some(IndividualExposure { who: n, vote: nominator_v }) + Some(IndividualExposure { who: n.to_owned(), vote: nominator_v }) } else { None } diff --git a/pallet/staking/src/tests.rs b/pallet/staking/src/tests.rs index 690f525ce..0810d4977 100644 --- a/pallet/staking/src/tests.rs +++ b/pallet/staking/src/tests.rs @@ -419,62 +419,62 @@ fn set_collator_count_should_work() { #[test] fn power_should_work() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(Staking::power_of(&1), 0); - assert_eq!(Staking::power_of(&2), 0); - assert_eq!(Staking::power_of(&3), 0); - assert_eq!(Staking::power_of(&4), 0); + assert_eq!(Staking::quick_power_of(&1), 0); + assert_eq!(Staking::quick_power_of(&2), 0); + assert_eq!(Staking::quick_power_of(&3), 0); + assert_eq!(Staking::quick_power_of(&4), 0); // 1 stakes 1 RING. assert_ok!(Staking::stake(RuntimeOrigin::signed(1), UNIT, 0, Vec::new())); - assert_eq!(Staking::power_of(&1), 500_000_000); + assert_eq!(Staking::quick_power_of(&1), 500_000_000); // 2 stakes 1 KTON. assert_ok!(Staking::stake(RuntimeOrigin::signed(2), 0, UNIT, Vec::new())); - assert_eq!(Staking::power_of(&1), 500_000_000); - assert_eq!(Staking::power_of(&2), 500_000_000); + assert_eq!(Staking::quick_power_of(&1), 500_000_000); + assert_eq!(Staking::quick_power_of(&2), 500_000_000); // 3 stakes 1 deposit. assert_ok!(Deposit::lock(RuntimeOrigin::signed(3), UNIT, 1)); assert_ok!(Staking::stake(RuntimeOrigin::signed(3), 0, 0, vec![0])); - assert_eq!(Staking::power_of(&1), 250_000_000); - assert_eq!(Staking::power_of(&2), 500_000_000); - assert_eq!(Staking::power_of(&3), 250_000_000); + assert_eq!(Staking::quick_power_of(&1), 250_000_000); + assert_eq!(Staking::quick_power_of(&2), 500_000_000); + assert_eq!(Staking::quick_power_of(&3), 250_000_000); // 4 stakes 1 KTON. assert_ok!(Staking::stake(RuntimeOrigin::signed(4), 0, UNIT, Vec::new())); - assert_eq!(Staking::power_of(&1), 250_000_000); - assert_eq!(Staking::power_of(&2), 250_000_000); - assert_eq!(Staking::power_of(&3), 250_000_000); - assert_eq!(Staking::power_of(&4), 250_000_000); + assert_eq!(Staking::quick_power_of(&1), 250_000_000); + assert_eq!(Staking::quick_power_of(&2), 250_000_000); + assert_eq!(Staking::quick_power_of(&3), 250_000_000); + assert_eq!(Staking::quick_power_of(&4), 250_000_000); // 1 unstakes 1 RING. assert_ok!(Staking::unstake(RuntimeOrigin::signed(1), UNIT, 0, Vec::new())); - assert_eq!(Staking::power_of(&1), 0); - assert_eq!(Staking::power_of(&2), 250_000_000); - assert_eq!(Staking::power_of(&3), 500_000_000); - assert_eq!(Staking::power_of(&4), 250_000_000); + assert_eq!(Staking::quick_power_of(&1), 0); + assert_eq!(Staking::quick_power_of(&2), 250_000_000); + assert_eq!(Staking::quick_power_of(&3), 500_000_000); + assert_eq!(Staking::quick_power_of(&4), 250_000_000); // 2 unstakes 1 KTON. assert_ok!(Staking::unstake(RuntimeOrigin::signed(2), 0, UNIT, Vec::new())); - assert_eq!(Staking::power_of(&1), 0); - assert_eq!(Staking::power_of(&2), 0); - assert_eq!(Staking::power_of(&3), 500_000_000); - assert_eq!(Staking::power_of(&4), 500_000_000); + assert_eq!(Staking::quick_power_of(&1), 0); + assert_eq!(Staking::quick_power_of(&2), 0); + assert_eq!(Staking::quick_power_of(&3), 500_000_000); + assert_eq!(Staking::quick_power_of(&4), 500_000_000); // 3 unstakes 1 deposit. assert_ok!(Deposit::lock(RuntimeOrigin::signed(3), UNIT, 1)); assert_ok!(Staking::unstake(RuntimeOrigin::signed(3), 0, 0, vec![0])); - assert_eq!(Staking::power_of(&1), 0); - assert_eq!(Staking::power_of(&2), 0); - assert_eq!(Staking::power_of(&3), 0); - assert_eq!(Staking::power_of(&4), 500_000_000); + assert_eq!(Staking::quick_power_of(&1), 0); + assert_eq!(Staking::quick_power_of(&2), 0); + assert_eq!(Staking::quick_power_of(&3), 0); + assert_eq!(Staking::quick_power_of(&4), 500_000_000); // 4 unstakes 1 KTON. assert_ok!(Staking::unstake(RuntimeOrigin::signed(4), 0, UNIT, Vec::new())); - assert_eq!(Staking::power_of(&1), 0); - assert_eq!(Staking::power_of(&2), 0); - assert_eq!(Staking::power_of(&3), 0); - assert_eq!(Staking::power_of(&4), 0); + assert_eq!(Staking::quick_power_of(&1), 0); + assert_eq!(Staking::quick_power_of(&2), 0); + assert_eq!(Staking::quick_power_of(&3), 0); + assert_eq!(Staking::quick_power_of(&4), 0); }); } diff --git a/pallet/staking/src/weights.rs b/pallet/staking/src/weights.rs index 139250360..7a3648fea 100644 --- a/pallet/staking/src/weights.rs +++ b/pallet/staking/src/weights.rs @@ -63,6 +63,7 @@ pub trait WeightInfo { fn nominate() -> Weight; fn chill() -> Weight; fn set_collator_count() -> Weight; + fn payout() -> Weight; } /// Weights for darwinia_staking using the Substrate node and recommended hardware. @@ -203,6 +204,9 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + fn payout() -> Weight { + Default::default() + } } // For backwards compatibility and tests @@ -342,4 +346,7 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(0, 0)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + fn payout() -> Weight { + Default::default() + } } From e8443ea939f106297dc1a879c5ed035b7019a6dc Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 12:34:27 +0800 Subject: [PATCH 06/15] Add migration --- pallet/staking/src/lib.rs | 1 - pallet/staking/src/migration.rs | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index fd10b2152..eb27c9326 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -32,7 +32,6 @@ #![deny(missing_docs)] #![deny(unused_crate_dependencies)] -/// Pallet migrations. pub mod migration; #[cfg(test)] diff --git a/pallet/staking/src/migration.rs b/pallet/staking/src/migration.rs index 8b1378917..32ddeca62 100644 --- a/pallet/staking/src/migration.rs +++ b/pallet/staking/src/migration.rs @@ -1 +1,102 @@ +//! Pallet migrations. +// core +use core::marker::PhantomData; +// darwinia +use crate::*; +// substrate +use frame_support::traits::OnRuntimeUpgrade; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + +/// Migration version 1. +pub mod v1 { + // darwinia + use super::*; + // substrate + use frame_support::storage::child::KillStorageResult; + + type AccountId = ::AccountId; + + #[frame_support::storage_alias] + type NextExposures = + StorageMap, Twox64Concat, AccountId, Exposure>>; + + #[frame_support::storage_alias] + type RewardPoints = + StorageValue, (u32, BTreeMap, u32>), ValueQuery>; + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] + struct ExposureV0 { + vote: Vote, + nominators: Vec>, + } + + /// Migrate darwinia-staking from v0 to v1. + pub struct MigrateToV1(PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 + where + T: Config, + { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + ensure!(StorageVersion::get::>() == 0, "Can only upgrade from version 0."); + + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + let version = StorageVersion::get::>(); + + if version != 0 { + log::warn!( + "[pallet::staking] skipping v0 to v1 migration: executed on wrong storage version. Expected version 1, found {version:?}", + ); + + return T::DbWeight::get().reads(1); + } + + let mut count = 4; + + >::translate::, _>(|c, v| { + count += 1; + + Some(Exposure { + commission: >::get(c).unwrap_or_default(), + vote: v.vote, + nominators: v.nominators, + }) + }); + #[allow(deprecated)] + { + count += match >::remove_all(None) { + KillStorageResult::AllRemoved(w) => w, + KillStorageResult::SomeRemaining(w) => w, + } as u64; + } + let (sum, map) = >::take(); + >::put(( + >::from(sum / 20), + map.into_iter() + .map(|(k, v)| (k, >::from(v / 20))) + .collect::>(), + )); + + StorageVersion::new(1).put::>(); + + T::DbWeight::get().reads_writes(count, count) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { + ensure!(StorageVersion::get::>() == 1, "Version must be upgraded."); + + // Check that everything decoded fine. + for k in >::iter_keys() { + ensure!(>::try_get(k).is_ok(), "Can not decode V1 `Exposure`."); + } + + Ok(()) + } + } +} From a4a55d032d0b46f54130ad1fa20e45e8bf95bc9a Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 17:20:37 +0800 Subject: [PATCH 07/15] Optimize DB RWs --- pallet/staking/Cargo.toml | 3 +- pallet/staking/src/benchmarking.rs | 4 +- pallet/staking/src/lib.rs | 147 +++++++++++++++--- pallet/staking/src/migration.rs | 49 +++--- pallet/staking/src/mock.rs | 16 +- pallet/staking/src/tests.rs | 26 ++-- runtime/crab/src/weights/darwinia_staking.rs | 3 + .../darwinia/src/weights/darwinia_staking.rs | 3 + .../pangolin/src/weights/darwinia_staking.rs | 3 + .../pangoro/src/weights/darwinia_staking.rs | 3 + 10 files changed, 199 insertions(+), 58 deletions(-) diff --git a/pallet/staking/Cargo.toml b/pallet/staking/Cargo.toml index 7f3b62e4d..6169da550 100644 --- a/pallet/staking/Cargo.toml +++ b/pallet/staking/Cargo.toml @@ -37,7 +37,7 @@ darwinia-deposit = { workspace = true, features = ["std"] } # substrate dc-inflation = { workspace = true, features = ["std"] } -pallet-assets = { workspace = true, features = ["runtime-benchmarks", "std"] } +pallet-assets = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-session = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true, features = ["std"] } @@ -80,6 +80,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/pallet/staking/src/benchmarking.rs b/pallet/staking/src/benchmarking.rs index b9789daa2..73d95e30d 100644 --- a/pallet/staking/src/benchmarking.rs +++ b/pallet/staking/src/benchmarking.rs @@ -203,7 +203,9 @@ mod benchmarks { let a = frame_benchmarking::whitelisted_caller::(); let sender = a.clone(); - >::insert( + assert_eq!(>::get().0, ExposureCacheState::Previous); + + >::insert( &a, Exposure { commission: Perbill::zero(), diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index eb27c9326..7574b64b7 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -62,6 +62,26 @@ use sp_runtime::{ }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +macro_rules! call_on_exposure { + ($c:expr, $s:ident$($f:tt)*) => {{ + match $c { + (ExposureCacheState::$s, _, _) => Ok(>$($f)*), + (_, ExposureCacheState::$s, _) => Ok(>$($f)*), + (_, _, ExposureCacheState::$s) => Ok(>$($f)*), + _ => { + log::error!("[pallet::staking] exposure cache states must be correct; qed"); + + Err("[pallet::staking] exposure cache states must be correct; qed") + }, + } + }}; + ($s:ident$($f:tt)*) => {{ + let c = >::get(); + + call_on_exposure!(c, $s$($f)*) + }}; +} + #[frame_support::pallet] pub mod pallet { // darwinia @@ -191,18 +211,62 @@ pub mod pallet { #[pallet::getter(fn collator_of)] pub type Collators = StorageMap<_, Twox64Concat, T::AccountId, Perbill>; - /// Previous stakers' exposure. + /// Exposure cache states. + /// + /// To avoid extra DB RWs during new session, such as: + /// ``` + /// previous = current; + /// current = next; + /// next = elect(); + /// ``` + /// + /// Now, with data: + /// ``` + /// cache1 == previous; + /// cache2 == current; + /// cache3 == next; + /// ``` + /// Just need to shift the marker and write the storage map once: + /// ``` + /// mark(cache3, current); + /// mark(cache2, previous); + /// mark(cache1, next); + /// cache1 = elect(); + /// ``` + #[pallet::storage] + #[pallet::getter(fn exposure_cache_states)] + pub type ExposureCacheStates = StorageValue< + _, + (ExposureCacheState, ExposureCacheState, ExposureCacheState), + ValueQuery, + ExposureCacheStatesDefault, + >; + /// Default value for [`ExposureCacheStates`]. + #[pallet::type_value] + pub fn ExposureCacheStatesDefault( + ) -> (ExposureCacheState, ExposureCacheState, ExposureCacheState) { + (ExposureCacheState::Previous, ExposureCacheState::Current, ExposureCacheState::Next) + } + + /// Exposure cache 0. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn previous_exposure_of)] - pub type PreviousExposures = + #[pallet::getter(fn exposure_cache_0_of)] + pub type ExposureCache0 = StorageMap<_, Twox64Concat, T::AccountId, Exposure>; - /// Active stakers' exposure. + /// Exposure cache 1. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn exposure_of)] - pub type Exposures = + #[pallet::getter(fn exposure_cache_1_of)] + pub type ExposureCache1 = + StorageMap<_, Twox64Concat, T::AccountId, Exposure>; + + /// Exposure cache 2. + #[pallet::storage] + #[pallet::unbounded] + #[pallet::getter(fn exposure_cache_2_of)] + pub type ExposureCache2 = StorageMap<_, Twox64Concat, T::AccountId, Exposure>; /// The ideal number of active collators. @@ -279,9 +343,10 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_: BlockNumberFor) -> Weight { - >::iter_keys() + call_on_exposure!(Previous::iter_keys() .take(1) - .fold(Zero::zero(), |acc, e| acc + Self::payout_inner(e).unwrap_or(Zero::zero())) + .fold(Zero::zero(), |acc, e| acc + Self::payout_inner(e).unwrap_or(Zero::zero()))) + .unwrap_or_default() } } #[pallet::call] @@ -857,7 +922,8 @@ pub mod pallet { /// Pay the reward to the collator and its nominators. pub fn payout_inner(collator: T::AccountId) -> Result { - let c_exposure = >::get(&collator).ok_or(>::NoReward)?; + let c_exposure = + call_on_exposure!(Previous::get(&collator).ok_or(>::NoReward)?)?; let c_total_payout = >::take(&collator).ok_or(>::NoReward)?; let mut c_payout = c_exposure.commission * c_total_payout; let n_payout = c_total_payout - c_payout; @@ -885,31 +951,47 @@ pub mod pallet { } /// Prepare the session state. - pub fn prepare_new_session(index: u32) -> Vec { - // Generally, this storage should be empty. - #[allow(deprecated)] - >::remove_all(None); - >::iter().drain().for_each(|(k, v)| { - >::insert(k, v); - }); + pub fn prepare_new_session(index: u32) -> Option> { + >::shift_exposure_cache_states(); log::info!( "[pallet::staking] assembling new collators for new session {index} at #{:?}", >::block_number(), ); - let collators = Self::elect(); + if let Ok(collators) = Self::elect() { + // TODO?: if we really need this event + Self::deposit_event(Event::Elected { collators: collators.clone() }); - // TODO?: if we really need this event - Self::deposit_event(Event::Elected { collators: collators.clone() }); + Some(collators) + } else { + // Impossible case. + // + // But if there is an issue, keep the old collators and do not update the session + // state to prevent the chain from stalling. + None + } + } - collators + /// Shift the exposure cache states. + /// + /// Previous Current Next + /// Next Previous Current + /// Current Next Previous + /// + /// ``` + /// loop { mutate(2, 0, 1) } + /// ``` + pub fn shift_exposure_cache_states() { + let (s0, s1, s2) = >::get(); + + >::put((s2, s0, s1)); } /// Elect the new collators. /// /// This should only be called by the [`pallet_session::SessionManager::new_session`]. - pub fn elect() -> Vec { + pub fn elect() -> Result, DispatchError> { let nominators = >::iter().collect::>(); let ring_pool = >::get(); let kton_pool = >::get(); @@ -938,13 +1020,18 @@ pub mod pallet { collators.sort_by(|(_, a), (_, b)| b.cmp(a)); + let cache_states = >::get(); + + #[allow(deprecated)] + call_on_exposure!(cache_states, Next::remove_all(None))?; + collators .into_iter() .take(>::get() as _) .map(|((c, e), _)| { - >::insert(&c, e); - - c + call_on_exposure!(cache_states, Next::insert(&c, e)) + .map(|_| c) + .map_err(Into::into) }) .collect() } @@ -1000,6 +1087,16 @@ where } } +/// Exposure cache's state. +#[allow(missing_docs)] +#[cfg_attr(feature = "runtime-benchmarks", derive(PartialEq))] +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] +pub enum ExposureCacheState { + Previous, + Current, + Next, +} + /// A convertor from collators id. Since this pallet does not have stash/controller, this is /// just identity. pub struct IdentityCollator; @@ -1087,7 +1184,7 @@ where fn start_session(_: u32) {} fn new_session(index: u32) -> Option> { - Some(Self::prepare_new_session(index)) + Self::prepare_new_session(index) } } diff --git a/pallet/staking/src/migration.rs b/pallet/staking/src/migration.rs index 32ddeca62..4237be7e6 100644 --- a/pallet/staking/src/migration.rs +++ b/pallet/staking/src/migration.rs @@ -13,14 +13,16 @@ use sp_runtime::TryRuntimeError; pub mod v1 { // darwinia use super::*; - // substrate - use frame_support::storage::child::KillStorageResult; type AccountId = ::AccountId; #[frame_support::storage_alias] type NextExposures = - StorageMap, Twox64Concat, AccountId, Exposure>>; + StorageMap, Twox64Concat, AccountId, ExposureV0>>; + + #[frame_support::storage_alias] + type Exposures = + StorageMap, Twox64Concat, AccountId, ExposureV0>>; #[frame_support::storage_alias] type RewardPoints = @@ -40,7 +42,7 @@ pub mod v1 { { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - ensure!(StorageVersion::get::>() == 0, "Can only upgrade from version 0."); + assert_eq!(StorageVersion::get::>(), 0, "Can only upgrade from version 0."); Ok(Vec::new()) } @@ -58,22 +60,31 @@ pub mod v1 { let mut count = 4; - >::translate::, _>(|c, v| { + >::iter().for_each(|(k, v)| { count += 1; - Some(Exposure { - commission: >::get(c).unwrap_or_default(), - vote: v.vote, - nominators: v.nominators, - }) + >::insert( + &k, + Exposure { + commission: >::get(&k).unwrap_or_default(), + vote: v.vote, + nominators: v.nominators, + }, + ); }); - #[allow(deprecated)] - { - count += match >::remove_all(None) { - KillStorageResult::AllRemoved(w) => w, - KillStorageResult::SomeRemaining(w) => w, - } as u64; - } + >::iter().for_each(|(k, v)| { + count += 1; + + >::insert( + &k, + Exposure { + commission: >::get(&k).unwrap_or_default(), + vote: v.vote, + nominators: v.nominators, + }, + ); + }); + let (sum, map) = >::take(); >::put(( >::from(sum / 20), @@ -89,11 +100,11 @@ pub mod v1 { #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { - ensure!(StorageVersion::get::>() == 1, "Version must be upgraded."); + assert_eq!(StorageVersion::get::>(), 1, "Version must be upgraded."); // Check that everything decoded fine. for k in >::iter_keys() { - ensure!(>::try_get(k).is_ok(), "Can not decode V1 `Exposure`."); + assert!(>::try_get(k).is_ok(), "Can not decode V1 `Exposure`."); } Ok(()) diff --git a/pallet/staking/src/mock.rs b/pallet/staking/src/mock.rs index a66b2c81a..ca6d79faf 100644 --- a/pallet/staking/src/mock.rs +++ b/pallet/staking/src/mock.rs @@ -25,6 +25,18 @@ use frame_support::traits::{Currency, OnInitialize}; use sp_io::TestExternalities; use sp_runtime::{BuildStorage, RuntimeAppPublic}; +#[macro_export] +macro_rules! call_on_exposure_test { + ($s:ident$($f:tt)*) => {{ + match >::get() { + (darwinia_staking::ExposureCacheState::$s, _, _) => >$($f)*, + (_, darwinia_staking::ExposureCacheState::$s, _) => >$($f)*, + (_, _, darwinia_staking::ExposureCacheState::$s) => >$($f)*, + _ => unreachable!(), + } + }}; +} + type BlockNumber = u64; type AccountId = u32; @@ -493,7 +505,7 @@ pub fn new_session() { } pub fn payout() { - >::iter_keys().for_each(|c| { + call_on_exposure_test!(Previous::iter_keys().for_each(|c| { let _ = Staking::payout_inner(c); - }); + })); } diff --git a/pallet/staking/src/tests.rs b/pallet/staking/src/tests.rs index 0810d4977..8789d396a 100644 --- a/pallet/staking/src/tests.rs +++ b/pallet/staking/src/tests.rs @@ -501,7 +501,7 @@ fn elect_should_work() { assert_ok!(Staking::nominate(RuntimeOrigin::signed(i), i - 5)); }); - assert_eq!(Staking::elect(), vec![5, 4, 3]); + assert_eq!(Staking::elect().unwrap(), vec![5, 4, 3]); }); ExtBuilder::default().collator_count(3).build().execute_with(|| { (1..=5).for_each(|i| { @@ -524,7 +524,7 @@ fn elect_should_work() { assert_ok!(Staking::nominate(RuntimeOrigin::signed(i), i - 5)); }); - assert_eq!(Staking::elect(), vec![1, 2, 3]); + assert_eq!(Staking::elect().unwrap(), vec![1, 2, 3]); }); } @@ -558,6 +558,7 @@ fn payout_should_work() { Efflux::time(session_duration - >::get() as Moment); Staking::note_authors(&[1, 2, 3, 4, 5]); new_session(); + new_session(); payout(); let rewards = vec![ @@ -611,6 +612,7 @@ fn payout_should_work() { assert_ok!(Staking::nominate(RuntimeOrigin::signed(i), i - 5)); }); new_session(); + new_session(); (1..=10).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); let total_issuance = Balances::total_issuance(); @@ -672,8 +674,9 @@ fn payout_should_work() { #[test] fn on_new_session_should_work() { ExtBuilder::default().collator_count(2).genesis_collator().build().execute_with(|| { - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); + assert_eq_uvec!(call_on_exposure_test!(Previous::iter_keys().collect::>()), [1, 2]); + assert_eq_uvec!(call_on_exposure_test!(Current::iter_keys().collect::>()), [1, 2]); + assert_eq_uvec!(call_on_exposure_test!(Next::iter_keys().collect::>()), [1, 2]); assert_ok!(Staking::collect(RuntimeOrigin::signed(3), Perbill::zero())); assert_ok!(Staking::stake(RuntimeOrigin::signed(3), 2 * UNIT, 0, Vec::new())); @@ -681,8 +684,9 @@ fn on_new_session_should_work() { Staking::note_authors(&Session::validators()); new_session(); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 2]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); + assert_eq_uvec!(call_on_exposure_test!(Previous::iter_keys().collect::>()), [1, 2]); + assert_eq_uvec!(call_on_exposure_test!(Current::iter_keys().collect::>()), [1, 2]); + assert_eq_uvec!(call_on_exposure_test!(Next::iter_keys().collect::>()), [1, 3]); assert_ok!(Staking::chill(RuntimeOrigin::signed(3))); assert_ok!(Staking::collect(RuntimeOrigin::signed(4), Perbill::zero())); @@ -691,8 +695,9 @@ fn on_new_session_should_work() { Staking::note_authors(&Session::validators()); new_session(); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 3]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); + assert_eq_uvec!(call_on_exposure_test!(Previous::iter_keys().collect::>()), [1, 2]); + assert_eq_uvec!(call_on_exposure_test!(Current::iter_keys().collect::>()), [1, 3]); + assert_eq_uvec!(call_on_exposure_test!(Next::iter_keys().collect::>()), [1, 4]); assert_ok!(Staking::chill(RuntimeOrigin::signed(4))); assert_ok!(Staking::collect(RuntimeOrigin::signed(5), Perbill::zero())); @@ -701,7 +706,8 @@ fn on_new_session_should_work() { Staking::note_authors(&Session::validators()); new_session(); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 4]); - assert_eq_uvec!(>::iter_keys().collect::>(), [1, 5]); + assert_eq_uvec!(call_on_exposure_test!(Previous::iter_keys().collect::>()), [1, 3]); + assert_eq_uvec!(call_on_exposure_test!(Current::iter_keys().collect::>()), [1, 4]); + assert_eq_uvec!(call_on_exposure_test!(Next::iter_keys().collect::>()), [1, 5]); }); } diff --git a/runtime/crab/src/weights/darwinia_staking.rs b/runtime/crab/src/weights/darwinia_staking.rs index af73a5e85..6416df994 100644 --- a/runtime/crab/src/weights/darwinia_staking.rs +++ b/runtime/crab/src/weights/darwinia_staking.rs @@ -199,4 +199,7 @@ impl darwinia_staking::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } + fn payout() -> Weight { + Default::default() + } } diff --git a/runtime/darwinia/src/weights/darwinia_staking.rs b/runtime/darwinia/src/weights/darwinia_staking.rs index 8c7cb0b94..4f2033b11 100644 --- a/runtime/darwinia/src/weights/darwinia_staking.rs +++ b/runtime/darwinia/src/weights/darwinia_staking.rs @@ -199,4 +199,7 @@ impl darwinia_staking::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } + fn payout() -> Weight { + Default::default() + } } diff --git a/runtime/pangolin/src/weights/darwinia_staking.rs b/runtime/pangolin/src/weights/darwinia_staking.rs index 5a63951e5..f7d285884 100644 --- a/runtime/pangolin/src/weights/darwinia_staking.rs +++ b/runtime/pangolin/src/weights/darwinia_staking.rs @@ -199,4 +199,7 @@ impl darwinia_staking::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } + fn payout() -> Weight { + Default::default() + } } diff --git a/runtime/pangoro/src/weights/darwinia_staking.rs b/runtime/pangoro/src/weights/darwinia_staking.rs index 1a951ad2b..53d0521ed 100644 --- a/runtime/pangoro/src/weights/darwinia_staking.rs +++ b/runtime/pangoro/src/weights/darwinia_staking.rs @@ -199,4 +199,7 @@ impl darwinia_staking::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } + fn payout() -> Weight { + Default::default() + } } From 97d1f8ed0824a7f32a13d304e6db5df684f45863 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 17:22:28 +0800 Subject: [PATCH 08/15] Typo --- pallet/staking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 7574b64b7..a48a21a7d 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -155,7 +155,7 @@ pub mod pallet { who: T::AccountId, commission: Perbill, }, - /// A payout_inner has been made for the staker. + /// A payout has been made for the staker. Payout { staker: T::AccountId, amount: Balance, From 591c92e5ae5a13f273511befc4efb07137925477 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 17:37:12 +0800 Subject: [PATCH 09/15] More checks --- pallet/staking/src/lib.rs | 3 ++- pallet/staking/src/migration.rs | 46 +++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index a48a21a7d..7bd78d05b 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -344,6 +344,7 @@ pub mod pallet { impl Hooks> for Pallet { fn on_initialize(_: BlockNumberFor) -> Weight { call_on_exposure!(Previous::iter_keys() + // TODO?: make this value adjustable .take(1) .fold(Zero::zero(), |acc, e| acc + Self::payout_inner(e).unwrap_or(Zero::zero()))) .unwrap_or_default() @@ -1089,7 +1090,7 @@ where /// Exposure cache's state. #[allow(missing_docs)] -#[cfg_attr(feature = "runtime-benchmarks", derive(PartialEq))] +#[cfg_attr(any(feature = "runtime-benchmarks", feature = "try-runtime"), derive(PartialEq))] #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)] pub enum ExposureCacheState { Previous, diff --git a/pallet/staking/src/migration.rs b/pallet/staking/src/migration.rs index 4237be7e6..377e61dd7 100644 --- a/pallet/staking/src/migration.rs +++ b/pallet/staking/src/migration.rs @@ -60,6 +60,14 @@ pub mod v1 { let mut count = 4; + let (sum, map) = >::take(); + >::put(( + >::from(sum / 20), + map.into_iter() + .map(|(k, v)| (k, >::from(v / 20))) + .collect::>(), + )); + >::iter().for_each(|(k, v)| { count += 1; @@ -85,14 +93,6 @@ pub mod v1 { ); }); - let (sum, map) = >::take(); - >::put(( - >::from(sum / 20), - map.into_iter() - .map(|(k, v)| (k, >::from(v / 20))) - .collect::>(), - )); - StorageVersion::new(1).put::>(); T::DbWeight::get().reads_writes(count, count) @@ -102,10 +102,34 @@ pub mod v1 { fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { assert_eq!(StorageVersion::get::>(), 1, "Version must be upgraded."); + // Old storages should be killed. + assert_eq!(>::iter_keys().count(), 0); + assert_eq!(>::iter_keys().count(), 0); + assert_eq!(!>::exist()); + + // Check the starting state is correct. + assert_eq!( + >::get(), + ( + ExposureCacheState::Previous, + ExposureCacheState::Current, + ExposureCacheState::Next + ) + ); + + assert_eq!( + >::iter_keys().count(), + 0, + "Previous exposure should be empty at start." + ); + // Check that everything decoded fine. - for k in >::iter_keys() { - assert!(>::try_get(k).is_ok(), "Can not decode V1 `Exposure`."); - } + >::iter_keys().for_each(|k| { + assert!(>::try_get(k).is_ok(), "Can not decode V1 `Exposure`."); + }); + >::iter_keys().for_each(|k| { + assert!(>::try_get(k).is_ok(), "Can not decode V1 `Exposure`."); + }); Ok(()) } From 32d9bad6fbb49fc4e2da98d0a0eb08fd56d79614 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Tue, 14 Nov 2023 22:31:48 +0800 Subject: [PATCH 10/15] Fix --- pallet/staking/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 7bd78d05b..fe0f5ce94 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -214,20 +214,20 @@ pub mod pallet { /// Exposure cache states. /// /// To avoid extra DB RWs during new session, such as: - /// ``` + /// ```nocompile /// previous = current; /// current = next; /// next = elect(); /// ``` /// /// Now, with data: - /// ``` + /// ```nocompile /// cache1 == previous; /// cache2 == current; /// cache3 == next; /// ``` /// Just need to shift the marker and write the storage map once: - /// ``` + /// ```nocompile /// mark(cache3, current); /// mark(cache2, previous); /// mark(cache1, next); @@ -980,7 +980,7 @@ pub mod pallet { /// Next Previous Current /// Current Next Previous /// - /// ``` + /// ```nocompile /// loop { mutate(2, 0, 1) } /// ``` pub fn shift_exposure_cache_states() { From f7da6614fe614b3070e494049ed5014aa26b7fb9 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Wed, 15 Nov 2023 00:37:52 +0800 Subject: [PATCH 11/15] Naming --- pallet/staking/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index fe0f5ce94..c0cda5ee5 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -63,8 +63,8 @@ use sp_runtime::{ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; macro_rules! call_on_exposure { - ($c:expr, $s:ident$($f:tt)*) => {{ - match $c { + ($s_e:expr, $s:ident$($f:tt)*) => {{ + match $s_e { (ExposureCacheState::$s, _, _) => Ok(>$($f)*), (_, ExposureCacheState::$s, _) => Ok(>$($f)*), (_, _, ExposureCacheState::$s) => Ok(>$($f)*), @@ -76,9 +76,9 @@ macro_rules! call_on_exposure { } }}; ($s:ident$($f:tt)*) => {{ - let c = >::get(); + let s = >::get(); - call_on_exposure!(c, $s$($f)*) + call_on_exposure!(s, $s$($f)*) }}; } From a9b2e24b6cb9235d29457d79708377f5bc4863e4 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Wed, 15 Nov 2023 00:39:34 +0800 Subject: [PATCH 12/15] Doc --- pallet/staking/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index c0cda5ee5..40110c482 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -968,8 +968,8 @@ pub mod pallet { } else { // Impossible case. // - // But if there is an issue, keep the old collators and do not update the session - // state to prevent the chain from stalling. + // But if there is an issue, retain the old collators; do not alter the session + // collators if any error occurs to prevent the chain from stalling. None } } From bf9c278322ad65be63dcff1f5ad8df94ff66dfc8 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Wed, 15 Nov 2023 14:28:24 +0800 Subject: [PATCH 13/15] Some fixes --- pallet/staking/src/lib.rs | 1 + pallet/staking/src/migration.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 40110c482..324d04d08 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -345,6 +345,7 @@ pub mod pallet { fn on_initialize(_: BlockNumberFor) -> Weight { call_on_exposure!(Previous::iter_keys() // TODO?: make this value adjustable + .drain() .take(1) .fold(Zero::zero(), |acc, e| acc + Self::payout_inner(e).unwrap_or(Zero::zero()))) .unwrap_or_default() diff --git a/pallet/staking/src/migration.rs b/pallet/staking/src/migration.rs index 377e61dd7..8c496d8f8 100644 --- a/pallet/staking/src/migration.rs +++ b/pallet/staking/src/migration.rs @@ -68,7 +68,7 @@ pub mod v1 { .collect::>(), )); - >::iter().for_each(|(k, v)| { + >::iter().drain().for_each(|(k, v)| { count += 1; >::insert( @@ -80,7 +80,7 @@ pub mod v1 { }, ); }); - >::iter().for_each(|(k, v)| { + >::iter().drain().for_each(|(k, v)| { count += 1; >::insert( @@ -105,7 +105,7 @@ pub mod v1 { // Old storages should be killed. assert_eq!(>::iter_keys().count(), 0); assert_eq!(>::iter_keys().count(), 0); - assert_eq!(!>::exist()); + assert!(!>::exists()); // Check the starting state is correct. assert_eq!( From f468882669f219cd700a9f7ffc519efb49a4489b Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Thu, 16 Nov 2023 01:34:28 +0800 Subject: [PATCH 14/15] Some updates --- pallet/staking/src/benchmarking.rs | 2 +- pallet/staking/src/lib.rs | 76 ++++++++++++++---------------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/pallet/staking/src/benchmarking.rs b/pallet/staking/src/benchmarking.rs index 73d95e30d..f218f10d0 100644 --- a/pallet/staking/src/benchmarking.rs +++ b/pallet/staking/src/benchmarking.rs @@ -218,7 +218,7 @@ mod benchmarks { .collect(), }, ); - >::insert(&a, 500); + >::insert(&a, 500); #[extrinsic_call] _(RawOrigin::Signed(sender), a); diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 324d04d08..9feb2b4ec 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -56,6 +56,7 @@ use frame_support::{ pallet_prelude::*, traits::Currency, DefaultNoBound, EqNoBound, PalletId, PartialEqNoBound, }; use frame_system::{pallet_prelude::*, RawOrigin}; +use pallet_session::ShouldEndSession; use sp_runtime::{ traits::{AccountIdConversion, Convert, One, Zero}, Perbill, Perquintill, @@ -98,7 +99,7 @@ pub mod pallet { pub trait DepositConfig {} #[pallet::config] - pub trait Config: frame_system::Config + DepositConfig { + pub trait Config: frame_system::Config + pallet_session::Config + DepositConfig { /// Override the [`frame_system::Config::RuntimeEvent`]. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -287,11 +288,11 @@ pub mod pallet { pub type AuthoredBlocksCount = StorageValue<_, (BlockNumberFor, BTreeMap>), ValueQuery>; - /// All outstanding rewards since the last payment.. + /// All outstanding rewards since the last payment. #[pallet::storage] #[pallet::unbounded] - #[pallet::getter(fn unpaid)] - pub type Unpaid = StorageMap<_, Twox64Concat, T::AccountId, Balance>; + #[pallet::getter(fn pending_reward_of)] + pub type PendingRewards = StorageMap<_, Twox64Concat, T::AccountId, Balance>; /// Active session's start-time. #[pallet::storage] @@ -342,13 +343,20 @@ pub mod pallet { pub struct Pallet(PhantomData); #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(_: BlockNumberFor) -> Weight { - call_on_exposure!(Previous::iter_keys() - // TODO?: make this value adjustable - .drain() - .take(1) - .fold(Zero::zero(), |acc, e| acc + Self::payout_inner(e).unwrap_or(Zero::zero()))) - .unwrap_or_default() + fn on_initialize(now: BlockNumberFor) -> Weight { + // There are already plenty of tasks to handle during the new session, + // so refrain from assigning any additional ones here. + if !::ShouldEndSession::should_end_session(now) { + call_on_exposure!(Previous::iter_keys() + // TODO?: make this value adjustable + .drain() + .take(1) + .fold(Zero::zero(), |acc, e| acc + + Self::payout_inner(e).unwrap_or(Zero::zero()))) + .unwrap_or_default() + } else { + Zero::zero() + } } } #[pallet::call] @@ -841,28 +849,7 @@ pub mod pallet { /// Calculate the power of the given account. #[cfg(any(feature = "runtime-benchmarks", test))] pub fn quick_power_of(who: &T::AccountId) -> Power { - // Power is a mixture of RING and KTON. - // - `total_ring_power = (amount / total_staked_ring) * HALF_POWER` - // - `total_kton_power = (amount / total_staked_kton) * HALF_POWER` - fn balance2power

(amount: Balance) -> Power - where - P: frame_support::StorageValue, - { - (Perquintill::from_rational(amount, P::get().max(1)) * 500_000_000_u128) as _ - } - - >::get(who) - .map(|l| { - balance2power::>( - l.staked_ring - + l.staked_deposits - .into_iter() - // We don't care if the deposit exists here. - // It was guaranteed by the `stake`/`unstake`/`restake` functions. - .fold(0, |r, d| r + T::Deposit::amount(who, d).unwrap_or_default()), - ) + balance2power::>(l.staked_kton) - }) - .unwrap_or_default() + Self::power_of(who, >::get(), >::get()) } /// Calculate the power of the given account. @@ -870,6 +857,10 @@ pub mod pallet { /// This is an optimized version of [`Self::quick_power_of`]. /// Avoiding read the pools' storage multiple times. pub fn power_of(who: &T::AccountId, ring_pool: Balance, kton_pool: Balance) -> Power { + // Power is a mixture of RING and KTON. + // - `total_ring_power = (amount / total_staked_ring) * HALF_POWER` + // - `total_kton_power = (amount / total_staked_kton) * HALF_POWER` + const HALF_POWER: u128 = 500_000_000; >::get(who) @@ -891,8 +882,8 @@ pub mod pallet { .unwrap_or_default() } - /// Pay the session reward to staking pot and update the stakers' reward record. - pub fn dispatch_session_reward(amount: Balance) -> Balance { + /// Distribute the session reward to staking pot and update the stakers' reward record. + pub fn distribute_session_reward(amount: Balance) -> Balance { let (sum, map) = >::take(); let staking_pot = account_id(); let mut actual_reward = 0; @@ -901,7 +892,7 @@ pub mod pallet { map.into_iter().for_each(|(c, p)| { let r = Perbill::from_rational(p, sum) * amount; - >::mutate(&c, |u| *u = u.map(|u| u + r).or(Some(r))); + >::mutate(&c, |u| *u = u.map(|u| u + r).or(Some(r))); if T::InflationManager::reward(&staking_pot, r).is_ok() { actual_reward += r; @@ -926,7 +917,8 @@ pub mod pallet { pub fn payout_inner(collator: T::AccountId) -> Result { let c_exposure = call_on_exposure!(Previous::get(&collator).ok_or(>::NoReward)?)?; - let c_total_payout = >::take(&collator).ok_or(>::NoReward)?; + let c_total_payout = + >::take(&collator).ok_or(>::NoReward)?; let mut c_payout = c_exposure.commission * c_total_payout; let n_payout = c_total_payout - c_payout; for n_exposure in c_exposure.nominators { @@ -956,6 +948,11 @@ pub mod pallet { pub fn prepare_new_session(index: u32) -> Option> { >::shift_exposure_cache_states(); + #[allow(deprecated)] + if call_on_exposure!(Next::remove_all(None)).is_err() { + return None; + } + log::info!( "[pallet::staking] assembling new collators for new session {index} at #{:?}", >::block_number(), @@ -1024,9 +1021,6 @@ pub mod pallet { let cache_states = >::get(); - #[allow(deprecated)] - call_on_exposure!(cache_states, Next::remove_all(None))?; - collators .into_iter() .take(>::get() as _) @@ -1055,7 +1049,7 @@ where fn on_session_end() { let inflation = Self::inflate(); let reward = Self::calculate_reward(inflation); - let actual_reward = >::dispatch_session_reward(reward); + let actual_reward = >::distribute_session_reward(reward); if inflation != 0 { Self::clear(inflation.saturating_sub(actual_reward)); From e8d550b237795b9b06e43ef69a9c1942d7172866 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Thu, 16 Nov 2023 02:11:56 +0800 Subject: [PATCH 15/15] Decouple --- pallet/account-migration/Cargo.toml | 2 +- pallet/account-migration/src/mock.rs | 1 + pallet/staking/src/lib.rs | 10 ++++++---- pallet/staking/src/mock.rs | 12 ++++++++++++ precompile/staking/src/mock.rs | 1 + runtime/crab/src/pallets/staking.rs | 13 +++++++++++++ runtime/darwinia/src/pallets/staking.rs | 13 +++++++++++++ runtime/pangolin/src/pallets/staking.rs | 13 +++++++++++++ runtime/pangoro/src/pallets/staking.rs | 13 +++++++++++++ 9 files changed, 73 insertions(+), 5 deletions(-) diff --git a/pallet/account-migration/Cargo.toml b/pallet/account-migration/Cargo.toml index eecf991ff..9239047ed 100644 --- a/pallet/account-migration/Cargo.toml +++ b/pallet/account-migration/Cargo.toml @@ -80,7 +80,7 @@ try-runtime = [ # darwinia "darwinia-deposit/try-runtime", "darwinia-staking/try-runtime", - + # substrate "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/pallet/account-migration/src/mock.rs b/pallet/account-migration/src/mock.rs index 8c52dc9e1..a0edb2558 100644 --- a/pallet/account-migration/src/mock.rs +++ b/pallet/account-migration/src/mock.rs @@ -169,6 +169,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = (); type Ring = Dummy; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = (); type WeightInfo = (); } #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 9feb2b4ec..030f46b07 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -56,7 +56,6 @@ use frame_support::{ pallet_prelude::*, traits::Currency, DefaultNoBound, EqNoBound, PalletId, PartialEqNoBound, }; use frame_system::{pallet_prelude::*, RawOrigin}; -use pallet_session::ShouldEndSession; use sp_runtime::{ traits::{AccountIdConversion, Convert, One, Zero}, Perbill, Perquintill, @@ -99,7 +98,7 @@ pub mod pallet { pub trait DepositConfig {} #[pallet::config] - pub trait Config: frame_system::Config + pallet_session::Config + DepositConfig { + pub trait Config: frame_system::Config + DepositConfig { /// Override the [`frame_system::Config::RuntimeEvent`]. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -121,6 +120,9 @@ pub mod pallet { /// Inflation and reward manager. type InflationManager: InflationManager; + /// Pass [`pallet_session::Config::ShouldEndSession`]'s result to here. + type ShouldEndSession: Get; + /// Minimum time to stake at least. #[pallet::constant] type MinStakingDuration: Get>; @@ -343,10 +345,10 @@ pub mod pallet { pub struct Pallet(PhantomData); #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(now: BlockNumberFor) -> Weight { + fn on_initialize(_: BlockNumberFor) -> Weight { // There are already plenty of tasks to handle during the new session, // so refrain from assigning any additional ones here. - if !::ShouldEndSession::should_end_session(now) { + if !T::ShouldEndSession::get() { call_on_exposure!(Previous::iter_keys() // TODO?: make this value adjustable .drain() diff --git a/pallet/staking/src/mock.rs b/pallet/staking/src/mock.rs index ca6d79faf..eabd547e1 100644 --- a/pallet/staking/src/mock.rs +++ b/pallet/staking/src/mock.rs @@ -350,6 +350,17 @@ impl darwinia_staking::InflationManager for OnCrabSessionEnd { ) } } +pub enum ShouldEndSession {} +impl frame_support::traits::Get for ShouldEndSession { + fn get() -> bool { + // substrate + use pallet_session::ShouldEndSession; + + ::ShouldEndSession::should_end_session( + System::block_number(), + ) + } +} impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; @@ -360,6 +371,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = frame_support::traits::ConstU64<3>; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = ShouldEndSession; type WeightInfo = (); } #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/precompile/staking/src/mock.rs b/precompile/staking/src/mock.rs index f2ba7a593..b78128593 100644 --- a/precompile/staking/src/mock.rs +++ b/precompile/staking/src/mock.rs @@ -241,6 +241,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = frame_support::traits::ConstU64<3>; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = (); type WeightInfo = (); } #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/runtime/crab/src/pallets/staking.rs b/runtime/crab/src/pallets/staking.rs index 1421998b3..39b62f56c 100644 --- a/runtime/crab/src/pallets/staking.rs +++ b/runtime/crab/src/pallets/staking.rs @@ -88,6 +88,18 @@ impl darwinia_staking::InflationManager for OnCrabSessionEnd { } } +pub enum ShouldEndSession {} +impl frame_support::traits::Get for ShouldEndSession { + fn get() -> bool { + // substrate + use pallet_session::ShouldEndSession; + + ::ShouldEndSession::should_end_session( + System::block_number(), + ) + } +} + impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; @@ -98,6 +110,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = MinStakingDuration; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = ShouldEndSession; type WeightInfo = weights::darwinia_staking::WeightInfo; } #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/runtime/darwinia/src/pallets/staking.rs b/runtime/darwinia/src/pallets/staking.rs index 2a8e76858..473598b21 100644 --- a/runtime/darwinia/src/pallets/staking.rs +++ b/runtime/darwinia/src/pallets/staking.rs @@ -105,6 +105,18 @@ impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { } } +pub enum ShouldEndSession {} +impl frame_support::traits::Get for ShouldEndSession { + fn get() -> bool { + // substrate + use pallet_session::ShouldEndSession; + + ::ShouldEndSession::should_end_session( + System::block_number(), + ) + } +} + impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; @@ -115,6 +127,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = MinStakingDuration; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = ShouldEndSession; type WeightInfo = weights::darwinia_staking::WeightInfo; } #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/runtime/pangolin/src/pallets/staking.rs b/runtime/pangolin/src/pallets/staking.rs index fa94b9936..28bf71b61 100644 --- a/runtime/pangolin/src/pallets/staking.rs +++ b/runtime/pangolin/src/pallets/staking.rs @@ -82,6 +82,18 @@ impl darwinia_staking::InflationManager for OnPangolinSessionEnd { } } +pub enum ShouldEndSession {} +impl frame_support::traits::Get for ShouldEndSession { + fn get() -> bool { + // substrate + use pallet_session::ShouldEndSession; + + ::ShouldEndSession::should_end_session( + System::block_number(), + ) + } +} + impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; @@ -92,6 +104,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = ConstU32<{ 2 * MINUTES }>; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = ShouldEndSession; type WeightInfo = weights::darwinia_staking::WeightInfo; } #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/runtime/pangoro/src/pallets/staking.rs b/runtime/pangoro/src/pallets/staking.rs index 5e760aa27..06e3f63b0 100644 --- a/runtime/pangoro/src/pallets/staking.rs +++ b/runtime/pangoro/src/pallets/staking.rs @@ -101,6 +101,18 @@ impl darwinia_staking::InflationManager for OnPangoroSessionEnd { } } +pub enum ShouldEndSession {} +impl frame_support::traits::Get for ShouldEndSession { + fn get() -> bool { + // substrate + use pallet_session::ShouldEndSession; + + ::ShouldEndSession::should_end_session( + System::block_number(), + ) + } +} + impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; @@ -111,6 +123,7 @@ impl darwinia_staking::Config for Runtime { type MinStakingDuration = ConstU32<{ 10 * MINUTES }>; type Ring = RingStaking; type RuntimeEvent = RuntimeEvent; + type ShouldEndSession = ShouldEndSession; type WeightInfo = weights::darwinia_staking::WeightInfo; } #[cfg(not(feature = "runtime-benchmarks"))]