diff --git a/pallets/parachain-staking/src/benchmarking.rs b/pallets/parachain-staking/src/benchmarking.rs index 1555c549da..235189666e 100644 --- a/pallets/parachain-staking/src/benchmarking.rs +++ b/pallets/parachain-staking/src/benchmarking.rs @@ -418,22 +418,22 @@ benchmarks! { // make sure delegator collated to collator let state = >::get(&collator).unwrap(); let delegator = state.delegators.into_bounded_vec()[0].owner.clone(); - assert_eq!(>::get(&delegator).unwrap().total, amount); + assert_eq!(>::get(&delegator).unwrap().amount, amount); // increase stake so we can unstake, because current stake is minimum T::Currency::make_free_balance_be(&delegator, T::CurrencyBalance::from(u128::MAX)); assert_ok!(>::delegator_stake_more(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), T::CurrencyBalance::from(u as u64))); - assert_eq!(>::get(&delegator).unwrap().total, amount + T::CurrencyBalance::from(u as u64)); + assert_eq!(>::get(&delegator).unwrap().amount, amount + T::CurrencyBalance::from(u as u64)); // fill unstake BTreeMap by unstaked many entries of 1 fill_unstaking::(&collator, Some(&delegator), u as u64); - assert_eq!(>::get(&delegator).unwrap().total, amount); + assert_eq!(>::get(&delegator).unwrap().amount, amount); let unlookup_collator = T::Lookup::unlookup(collator.clone()); }: _(RawOrigin::Signed(delegator.clone()), unlookup_collator, amount) verify { let state = >::get(&collator).unwrap(); assert!(state.delegators.into_iter().any(|x| x.owner == delegator)); - assert_eq!(>::get(&delegator).unwrap().total, amount + amount); + assert_eq!(>::get(&delegator).unwrap().amount, amount + amount); assert!(>::get(&delegator).is_empty()); } @@ -453,16 +453,16 @@ benchmarks! { // make sure delegator collated to collator let state = >::get(&collator).unwrap(); let delegator = state.delegators.into_bounded_vec()[0].owner.clone(); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get()); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get()); // increase stake so we can unstake, because current stake is minimum T::Currency::make_free_balance_be(&delegator, T::CurrencyBalance::from(u128::MAX)); assert_ok!(>::delegator_stake_more(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), amount + amount)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get() + amount + amount); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get() + amount + amount); // decrease stake once so we have an unstaking entry for this block assert_ok!(>::delegator_stake_less(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), amount)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get() + amount); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get() + amount); assert_eq!(>::get(&delegator).len(), 1); let unlookup_collator = T::Lookup::unlookup(collator.clone()); @@ -470,44 +470,7 @@ benchmarks! { verify { let state = >::get(&collator).unwrap(); assert!(state.delegators.into_iter().any(|x| x.owner == delegator)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get()); - assert_eq!(>::get(&delegator).len(), 2); - } - - revoke_delegation { - // we need at least 1 collators - let n in 1 .. T::MaxTopCandidates::get(); - // we need at least 1 delegator - let m in 1 .. T::MaxDelegatorsPerCollator::get() - 1; - - let candidates = setup_collator_candidates::(n, None); - for (i, c) in candidates.iter().enumerate() { - fill_delegators::(m, c.clone(), i.saturated_into::()); - } - let collator = candidates[0].clone(); - let amount = T::CurrencyBalance::one(); - - // make sure delegator collated to collator - let state = >::get(&collator).unwrap(); - let delegator = state.delegators.into_bounded_vec()[0].owner.clone(); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get()); - - // increase stake so we can unstake, because current stake is minimum - T::Currency::make_free_balance_be(&delegator, T::CurrencyBalance::from(u128::MAX)); - assert_ok!(>::delegator_stake_more(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), amount + amount)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get() + amount + amount); - - // decrease stake once so we have an unstaking entry for this block - assert_ok!(>::delegator_stake_less(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), amount)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get() + amount); - assert_eq!(>::get(&delegator).len(), 1); - let unlookup_collator = T::Lookup::unlookup(collator.clone()); - - }: _(RawOrigin::Signed(delegator.clone()), unlookup_collator) - verify { - let state = >::get(&collator).unwrap(); - assert!(!state.delegators.into_iter().any(|x| x.owner == delegator)); - assert!(>::get(&delegator).is_none()); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get()); assert_eq!(>::get(&delegator).len(), 2); } @@ -527,16 +490,16 @@ benchmarks! { // make sure delegator collated to collator let state = >::get(&collator).unwrap(); let delegator = state.delegators.into_bounded_vec()[0].owner.clone(); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get()); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get()); // increase stake so we can unstake, because current stake is minimum T::Currency::make_free_balance_be(&delegator, T::CurrencyBalance::from(u128::MAX)); assert_ok!(>::delegator_stake_more(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), amount + amount)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get() + amount + amount); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get() + amount + amount); // decrease stake once so we have an unstaking entry for this block assert_ok!(>::delegator_stake_less(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator.clone()), amount)); - assert_eq!(>::get(&delegator).unwrap().total, T::MinDelegatorStake::get() + amount); + assert_eq!(>::get(&delegator).unwrap().amount, T::MinDelegatorStake::get() + amount); assert_eq!(>::get(&delegator).len(), 1); }: _(RawOrigin::Signed(delegator.clone())) @@ -585,44 +548,6 @@ benchmarks! { verify { assert_eq!(>::get(), new); } - - // [Post-launch TODO]: Activate after increasing MaxCollatorsPerDelegator to at least 2. Expected to throw otherwise. - // delegate_another_candidate { - // // we need at least 2 collators - // let n in 2 .. T::MaxTopCandidates::get(); - // // we need at least 1 delegator - // let m in 1 .. T::MaxDelegatorsPerCollator::get() - 1; - // let u in 0 .. (T::MaxUnstakeRequests::get().saturated_into::() - 1); - - // let candidates = setup_collator_candidates::(n, None); - // for (i, c) in candidates.iter().enumerate() { - // fill_delegators::(m, c.clone(), i.saturated_into::()); - // } - // let collator_delegated = candidates[0].clone(); - // let collator = candidates.last().unwrap().clone(); - // let amount = T::MinDelegatorStake::get(); - - // // make sure delegator collated to collator_delegated - // let state_delegated = >::get(&collator_delegated).unwrap(); - // let delegator = state_delegated.delegators.into_bounded_vec()[0].owner.clone(); - // assert!(>::get(&delegator).is_some()); - - // // should not have delegated to collator yet - // let state = >::get(&collator).unwrap(); - // assert!(!state.delegators.into_iter().any(|x| x.owner == delegator)); - - // // increase stake so we can unstake, because current stake is minimum - // T::Currency::make_free_balance_be(&delegator, T::CurrencyBalance::from(u128::MAX)); - // assert_ok!(>::delegator_stake_more(RawOrigin::Signed(delegator.clone()).into(), T::Lookup::unlookup(collator_delegated.clone()), T::CurrencyBalance::from(u as u64))); - - // // fill unstake BTreeMap by unstaked many entries of 1 - // fill_unstaking::(&collator_delegated, Some(&delegator), u as u64); - - // }: _(RawOrigin::Signed(delegator.clone()), T::Lookup::unlookup(collator.clone()), amount) - // verify { - // let state = >::get(&collator).unwrap(); - // assert!(state.delegators.into_iter().any(|x| x.owner == delegator); - // } } impl_benchmark_test_suite!( diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 44ef3e2ccd..07ceef79b4 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -126,10 +126,8 @@ //! by the provided amount down to `MinCandidateStake`. //! - `join_delegators` - Join the set of delegators by delegating to a collator //! candidate. -//! - `delegate_another_candidate` - Delegate to another collator candidate by -//! staking for them. -//! - `leave_delegators` - Leave the set of delegators and revoke all -//! delegations. Since delegators do not have to run a node and cannot be +//! - `leave_delegators` - Leave the set of delegators and revoke your +//! delegation. Since delegators do not have to run a node and cannot be //! selected to become block authors, this exit is not delayed like it is for //! collator candidates. //! - `revoke_delegation` - Revoke a single delegation to a collator candidate. @@ -171,7 +169,6 @@ mod types; use frame_support::pallet; pub use crate::{default_weights::WeightInfo, pallet::*}; -use types::ReplacedDelegator; #[pallet] pub mod pallet { @@ -291,10 +288,6 @@ pub mod pallet { #[pallet::constant] type MaxDelegatorsPerCollator: Get + Debug + PartialEq; - /// Maximum number of collators a single delegator can delegate. - #[pallet::constant] - type MaxCollatorsPerDelegator: Get + Debug + PartialEq; - /// Maximum size of the top candidates set. #[pallet::constant] type MaxTopCandidates: Get + Debug + PartialEq; @@ -309,10 +302,6 @@ pub mod pallet { #[pallet::constant] type MinCollatorCandidateStake: Get>; - /// Minimum stake required for any account to be able to delegate. - #[pallet::constant] - type MinDelegation: Get>; - /// Minimum stake required for any account to become a delegator. #[pallet::constant] type MinDelegatorStake: Get>; @@ -368,8 +357,6 @@ pub mod pallet { ValStakeBelowMin, /// The account has already staked the maximum amount of funds possible. ValStakeAboveMax, - /// The account has not staked enough funds to become a delegator. - NomStakeBelowMin, /// The account has not staked enough funds to delegate a collator /// candidate. DelegationBelowMin, @@ -586,13 +573,8 @@ pub mod pallet { /// It maps from an account to its delegation details. #[pallet::storage] #[pallet::getter(fn delegator_state)] - pub(crate) type DelegatorState = StorageMap< - _, - Twox64Concat, - T::AccountId, - Delegator, T::MaxCollatorsPerDelegator>, - OptionQuery, - >; + pub(crate) type DelegatorState = + StorageMap<_, Twox64Concat, T::AccountId, Delegator>, OptionQuery>; /// The staking information for a candidate. /// @@ -758,12 +740,6 @@ pub mod pallet { /// ShouldEndSession<_>>::should_end_session. /// /// The dispatch origin must be Root. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account] - /// - Writes: ForceNewRound - /// # #[pallet::weight(::WeightInfo::set_inflation())] pub fn force_new_round(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; @@ -787,12 +763,6 @@ pub mod pallet { /// The dispatch origin must be Root. /// /// Emits `RoundInflationSet`. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account] - /// - Writes: InflationConfig - /// # #[pallet::weight(::WeightInfo::set_inflation())] pub fn set_inflation( origin: OriginFor, @@ -849,20 +819,6 @@ pub mod pallet { /// The dispatch origin must be Root. /// /// Emits `MaxSelectedCandidatesSet`. - /// - /// - /// # - /// - The transaction's complexity is mainly dependent on updating the - /// `SelectedCandidates` storage in `select_top_candidates` which in - /// return depends on the number of `MaxSelectedCandidates` (N). - /// - For each N, we read `CandidatePool` from the storage. - /// --------- - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators of a - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: MaxSelectedCandidates, TopCandidates, N * CandidatePool - /// - Writes: MaxSelectedCandidates - /// # #[pallet::weight(::WeightInfo::set_max_selected_candidates( *new, T::MaxDelegatorsPerCollator::get() @@ -931,12 +887,6 @@ pub mod pallet { /// The dispatch origin must be Root. /// /// Emits `BlocksPerRoundSet`. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account], Round - /// - Writes: Round - /// # #[pallet::weight(::WeightInfo::set_blocks_per_round())] pub fn set_blocks_per_round(origin: OriginFor, new: T::BlockNumber) -> DispatchResult { ensure_root(origin)?; @@ -966,12 +916,6 @@ pub mod pallet { /// The dispatch origin must be Root. /// /// Emits `MaxCandidateStakeChanged`. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account], MaxCollatorCandidateStake - /// - Writes: Round - /// # #[pallet::weight(::WeightInfo::set_max_candidate_stake())] pub fn set_max_candidate_stake(origin: OriginFor, new: BalanceOf) -> DispatchResult { ensure_root(origin)?; @@ -1000,23 +944,6 @@ pub mod pallet { /// Increments rewards of candidate and their delegators. /// /// Emits `CandidateRemoved`. - /// - /// # - /// - The transaction's complexity is mainly dependent on updating the - /// `SelectedCandidates` storage in `select_top_candidates` which in - /// return depends on the number of `MaxSelectedCandidates` (N). - /// - For each N, we read `CandidatePool` from the storage. - /// --------- - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators of the - /// collator candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: MaxCollatorCandidateStake, 2 * N * CandidatePool, - /// TopCandidates, BlockNumber, D * DelegatorState, D * Unstaking - /// - Writes: MaxCollatorCandidateStake, N * CandidatePool, D * - /// DelegatorState, (D + 1) * Unstaking - /// - Kills: CandidatePool, DelegatorState for all delegators which only - /// delegated to the candidate - /// # #[pallet::weight(::WeightInfo::force_remove_candidate( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get() @@ -1082,16 +1009,6 @@ pub mod pallet { /// candidates nor of the delegators set. /// /// Emits `JoinedCollatorCandidates`. - /// - /// # - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators for this - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: [Origin Account], DelegatorState, - /// MaxCollatorCandidateStake, Locks, TotalCollatorStake, - /// TopCandidates, MaxSelectedCandidates, CandidatePool, - /// - Writes: Locks, TotalCollatorStake, CandidatePool, TopCandidates, - /// # #[pallet::weight(::WeightInfo::join_candidates( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get() @@ -1165,20 +1082,6 @@ pub mod pallet { /// rewards, until the end of the next session. /// /// Emits `CollatorScheduledExit`. - /// - /// # - /// - The transaction's complexity is mainly dependent on updating the - /// `SelectedCandidates` storage in `select_top_candidates` which in - /// return depends on the number of `MaxSelectedCandidates` (N). - /// - For each N, we read `CandidatePool` from the storage. - /// --------- - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators for this - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: [Origin Account], TopCandidates, (N + 1) * CandidatePool, - /// TotalCollatorStake - /// - Writes: CandidatePool, TopCandidates, TotalCollatorStake - /// # #[pallet::weight(::WeightInfo::init_leave_candidates( T::MaxTopCandidates::get(), T::MaxTopCandidates::get().saturating_mul(T::MaxDelegatorsPerCollator::get()) @@ -1237,17 +1140,6 @@ pub mod pallet { /// `cancel_leave_candidates`. /// /// Emits `CollatorLeft`. - /// - /// # - /// Weight: O(N + D + U) where where N is `MaxSelectedCandidates` - /// bounded by `MaxTopCandidates`, D is the number of delegators for - /// this candidate bounded by `MaxDelegatorsPerCollator` and U is the - /// number of locked unstaking requests bounded by `MaxUnstakeRequests`. - /// - Reads: CandidatePool, Round, D * DelegatorState, D - /// * BlockNumber, D * Unstaking - /// - Writes: D * Unstaking, D * DelegatorState, Total - /// - Kills: CandidatePool, DelegatorState - /// # #[pallet::weight(::WeightInfo::execute_leave_candidates( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get(), @@ -1287,15 +1179,6 @@ pub mod pallet { /// `init_leave_candidates`. /// /// Emits `CollatorCanceledExit`. - /// - /// # - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators for this - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: [Origin Account], TotalCollatorStake, TopCandidates, - /// CandidatePool - /// - Writes: TotalCollatorStake, CandidatePool, TopCandidates - /// # #[pallet::weight(::WeightInfo::cancel_leave_candidates( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get(), @@ -1343,16 +1226,6 @@ pub mod pallet { /// allowed range as set in the pallet's configuration. /// /// Emits `CollatorStakedMore`. - /// - /// # - /// Weight: O(N + D + U) where where N is `MaxSelectedCandidates` - /// bounded by `MaxTopCandidates`, D is the number of delegators for - /// this candidate bounded by `MaxDelegatorsPerCollator` and U is the - /// number of locked unstaking requests bounded by `MaxUnstakeRequests`. - /// - Reads: [Origin Account], Locks, TotalCollatorStake, - /// MaxCollatorCandidateStake, TopCandidates, CandidatePool - /// - Writes: Locks, TotalCollatorStake, CandidatePool, TopCandidates - /// # #[pallet::weight(::WeightInfo::candidate_stake_more( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get(), @@ -1422,15 +1295,6 @@ pub mod pallet { /// allowed range as set in the pallet's configuration. /// /// Emits `CollatorStakedLess`. - /// - /// # - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators for this - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: [Origin Account], Unstaking, TopCandidates, - /// MaxSelectedCandidates, CandidatePool - /// - Writes: Unstaking, CandidatePool, TotalCollatorStake - /// # #[pallet::weight(::WeightInfo::candidate_stake_less( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get() @@ -1488,8 +1352,8 @@ pub mod pallet { /// The account that wants to delegate cannot be part of the collator /// candidates set as well. /// - /// The caller must _not_ have delegated before. Otherwise, - /// `delegate_another_candidate` should be called. + /// The caller must _not_ have a delegation. If that is the case, they + /// are required to first remove the delegation. /// /// The amount staked must be larger than the minimum required to become /// a delegator as set in the pallet's configuration. @@ -1505,16 +1369,6 @@ pub mod pallet { /// Emits `DelegationReplaced` if the candidate has /// `MaxDelegatorsPerCollator` many delegations but this delegator /// staked more than one of the other delegators of this candidate. - /// - /// # - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators for this - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: [Origin Account], DelegatorState, TopCandidates, - /// MaxSelectedCandidates, CandidatePool, LastDelegation, Round - /// - Writes: Locks, CandidatePool, DelegatorState, TotalCollatorStake, - /// LastDelegation - /// # #[pallet::weight(::WeightInfo::join_delegators( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get() @@ -1535,7 +1389,7 @@ pub mod pallet { // first delegation ensure!(DelegatorState::::get(&acc).is_none(), Error::::AlreadyDelegating); - ensure!(amount >= T::MinDelegatorStake::get(), Error::::NomStakeBelowMin); + ensure!(amount >= T::MinDelegatorStake::get(), Error::::DelegationBelowMin); // cannot be a collator candidate and delegator with same AccountId ensure!(Self::is_active_candidate(&acc).is_none(), Error::::CandidateExists); @@ -1568,11 +1422,10 @@ pub mod pallet { // should never fail but let's be safe ensure!(insert_delegator, Error::::DelegatorExists); - // can only throw if MaxCollatorsPerDelegator is set to 0 which should never - // occur in practice, even if the delegator rewards are set to 0 - let delegator_state = Delegator::try_new(collator.clone(), amount) - .map_err(|_| Error::::MaxCollatorsPerDelegatorExceeded)?; - + let delegator_state = Delegator { + amount, + owner: Some(collator.clone()), + }; let CandidateOf:: { stake: old_stake, total: old_total, @@ -1581,12 +1434,11 @@ pub mod pallet { // update state and potentially prepare kicking a delegator with less staked // amount (includes setting rewards for kicked delegator) - let (state, maybe_kicked_delegator) = if num_delegations_pre_insertion == T::MaxDelegatorsPerCollator::get() - { + let state = if num_delegations_pre_insertion == T::MaxDelegatorsPerCollator::get() { Self::do_update_delegator(delegation, state)? } else { state.total = state.total.saturating_add(amount); - (state, None) + state }; let new_total = state.total; @@ -1617,9 +1469,6 @@ pub mod pallet { // initiate reward counter to match the current state of the candidate RewardCount::::insert(&acc, RewardCount::::get(&collator)); - // update or clear storage of potentially kicked delegator - Self::update_kicked_delegator_storage(maybe_kicked_delegator); - Self::deposit_event(Event::Delegation(acc, amount, collator, new_total)); Ok(Some(::WeightInfo::join_delegators( n, @@ -1628,166 +1477,8 @@ pub mod pallet { .into()) } - /// Delegate another collator's candidate by staking some funds and - /// increasing the pallet's as well as the collator's total stake. - /// - /// The account that wants to delegate cannot be part of the collator - /// candidates set as well. - /// - /// The caller _must_ have delegated before. Otherwise, - /// `join_delegators` should be called. - /// - /// If the delegator has already delegated the maximum number of - /// collator candidates, this operation will fail. - /// - /// The amount staked must be larger than the minimum required to become - /// a delegator as set in the pallet's configuration. - /// - /// As only `MaxDelegatorsPerCollator` are allowed to delegate a given - /// collator, the amount staked must be larger than the lowest one in - /// the current set of delegator for the operation to be meaningful. - /// - /// The collator's total stake as well as the pallet's total stake are - /// increased accordingly. - /// - /// NOTE: This transaction is expected to throw until we increase - /// `MaxCollatorsPerDelegator` by at least one, since it is currently - /// set to one. - /// - /// Emits `Delegation`. - /// Emits `DelegationReplaced` if the candidate has - /// `MaxDelegatorsPerCollator` many delegations but this delegator - /// staked more than one of the other delegators of this candidate. - /// - /// # - /// Weight: O(N + D) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` and D is the number of delegators for this - /// candidate bounded by `MaxDelegatorsPerCollator`. - /// - Reads: [Origin Account], DelegatorState, TopCandidates, - /// MaxSelectedCandidates, CandidatePool, LastDelegation, Round - /// - Writes: Locks, CandidatePool, DelegatorState, TotalCollatorStake, - /// LastDelegation - /// # - // - // NOTE: We can't benchmark this extrinsic until we have increased `MaxCollatorsPerDelegator` by at least 1, - // thus we use the closest weight we can get. - #[pallet::weight(::WeightInfo::join_delegators( - T::MaxTopCandidates::get(), - T::MaxDelegatorsPerCollator::get() - ))] - pub fn delegate_another_candidate( - origin: OriginFor, - collator: ::Source, - amount: BalanceOf, - ) -> DispatchResultWithPostInfo { - let acc = ensure_signed(origin)?; - let collator = T::Lookup::lookup(collator)?; - let mut delegator = DelegatorState::::get(&acc).ok_or(Error::::NotYetDelegating)?; - - // check balance - ensure!( - pallet_balances::Pallet::::free_balance(acc.clone()) - >= delegator.total.saturating_add(amount).into(), - pallet_balances::Error::::InsufficientBalance - ); - - // delegation after first - ensure!(amount >= T::MinDelegation::get(), Error::::DelegationBelowMin); - ensure!( - (delegator.delegations.len().saturated_into::()) < T::MaxCollatorsPerDelegator::get(), - Error::::MaxCollatorsPerDelegatorExceeded - ); - // cannot delegate if number of delegations in this round exceeds - // MaxDelegationsPerRound - let delegation_counter = Self::get_delegation_counter(&acc)?; - - // prepare new collator state - let mut state = CandidatePool::::get(&collator).ok_or(Error::::CandidateNotFound)?; - let num_delegations_pre_insertion: u32 = state.delegators.len().saturated_into(); - ensure!(!state.is_leaving(), Error::::CannotDelegateIfLeaving); - - // attempt to insert delegation, check for uniqueness and update total delegated - // amount - // NOTE: excess is handled below because we support replacing a delegator - // with fewer stake - ensure!( - delegator - .add_delegation(Stake { - owner: collator.clone(), - amount - }) - .unwrap_or(true), - Error::::AlreadyDelegatedCollator - ); - let delegation = Stake { - owner: acc.clone(), - amount, - }; - - // throws if delegation insertion exceeds bounded vec limit which we will handle - // below in Self::do_update_delegator - ensure!( - state.delegators.try_insert(delegation.clone()).unwrap_or(true), - Error::::DelegatorExists - ); - - let CandidateOf:: { - stake: old_stake, - total: old_total, - .. - } = state; - - // update state and potentially prepare kicking a delegator with less staked - // amount (includes reward setting for kicked delegator) - let (state, maybe_kicked_delegator) = if num_delegations_pre_insertion == T::MaxDelegatorsPerCollator::get() - { - Self::do_update_delegator(delegation, state)? - } else { - state.total = state.total.saturating_add(amount); - (state, None) - }; - let new_total = state.total; - - // *** No Fail except during increase_lock beyond this point *** - - // lock stake - Self::increase_lock(&acc, delegator.total, amount)?; - - // update top candidates and total amount at stake - let n = if state.is_active() { - Self::update_top_candidates( - collator.clone(), - old_stake, - // safe because total >= stake - old_total - old_stake, - state.stake, - state.total - state.stake, - ) - } else { - 0u32 - }; - - // update states - CandidatePool::::insert(&collator, state); - DelegatorState::::insert(&acc, delegator); - >::insert(&acc, delegation_counter); - - // initiate reward counter to match the current state of the candidate - RewardCount::::insert(&acc, RewardCount::::get(&collator)); - - // update or clear storage of potentially kicked delegator - Self::update_kicked_delegator_storage(maybe_kicked_delegator); - - Self::deposit_event(Event::Delegation(acc, amount, collator, new_total)); - Ok(Some(::WeightInfo::join_delegators( - n, - T::MaxDelegatorsPerCollator::get(), - )) - .into()) - } - - /// Leave the set of delegators and, by implication, revoke all ongoing - /// delegations. + /// Leave the set of delegators and, by implication, revoke the ongoing + /// delegation. /// /// All staked funds are not unlocked immediately, but they are added to /// the queue of pending unstaking, and will effectively be released @@ -1798,19 +1489,10 @@ pub mod pallet { /// their chances to be included in the set of candidates in the next /// rounds. /// - /// Automatically increments the accumulated rewards of the origin for - /// each delegation. + /// Automatically increments the accumulated rewards of the origin of the + /// current delegation. /// /// Emits `DelegatorLeft`. - /// - /// # - /// Weight: O(C) where C is the number of delegations for this delegator - /// which is bounded by by `MaxCollatorsPerDelegator`. - /// - Reads: [Origin Account], DelegatorState, BlockNumber, Unstaking, - /// TopCandidates, MaxSelectedCandidates, C * CandidatePool, - /// - Writes: Unstaking, CandidatePool, TotalCollatorStake, - /// - Kills: DelegatorState - /// # #[pallet::weight(::WeightInfo::leave_delegators( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get() @@ -1818,66 +1500,17 @@ pub mod pallet { pub fn leave_delegators(origin: OriginFor) -> DispatchResultWithPostInfo { let acc = ensure_signed(origin)?; let delegator = DelegatorState::::get(&acc).ok_or(Error::::DelegatorNotFound)?; - let num_delegations: u32 = delegator.delegations.len().saturated_into(); - - // remove delegations and increment rewards - for stake in delegator.delegations.into_iter() { - Self::delegator_leaves_collator(acc.clone(), stake.owner.clone())?; - } + // should never throw + let collator = delegator.owner.ok_or(Error::::DelegationNotFound)?; + Self::delegator_leaves_collator(acc.clone(), collator)?; // *** No Fail beyond this point *** DelegatorState::::remove(&acc); - Self::deposit_event(Event::DelegatorLeft(acc, delegator.total)); + Self::deposit_event(Event::DelegatorLeft(acc, delegator.amount)); Ok(Some(::WeightInfo::leave_delegators( - num_delegations, - T::MaxDelegatorsPerCollator::get(), - )) - .into()) - } - - /// Terminates an ongoing delegation for a given collator candidate. - /// - /// The staked funds are not unlocked immediately, but they are added to - /// the queue of pending unstaking, and will effectively be released - /// after `StakeDuration` blocks from the moment the delegation is - /// terminated. - /// - /// This operation reduces the total stake of the pallet as well as the - /// stakes of the collator involved, potentially affecting its chances - /// to be included in the set of candidates in the next rounds. - /// - /// Emits `DelegatorLeft`. - /// - /// # - /// Weight: O(C) where C is the number of delegations for this delegator - /// which is bounded by by `MaxCollatorsPerDelegator`. - /// - Reads: [Origin Account], DelegatorState, BlockNumber, Unstaking, - /// Locks, TopCandidates, CandidatePool, MaxSelectedCandidates - /// - Writes: Unstaking, Locks, DelegatorState, CandidatePool, - /// TotalCollatorStake - /// - Kills: DelegatorState if the delegator has not delegated to - /// another collator - /// # - #[pallet::weight(::WeightInfo::revoke_delegation( - T::MaxCollatorsPerDelegator::get(), - T::MaxDelegatorsPerCollator::get() - ))] - pub fn revoke_delegation( - origin: OriginFor, - collator: ::Source, - ) -> DispatchResultWithPostInfo { - let collator = T::Lookup::lookup(collator)?; - let delegator = ensure_signed(origin)?; - - // *** No Fail except during delegator_revokes_collator beyond this point *** - - // revoke delegation and increment rewards - let num_delegations = Self::delegator_revokes_collator(delegator, collator)?; - - Ok(Some(::WeightInfo::revoke_delegation( - num_delegations, + 1, T::MaxDelegatorsPerCollator::get(), )) .into()) @@ -1889,17 +1522,6 @@ pub mod pallet { /// collator candidate to be added to it. /// /// Emits `DelegatorStakedMore`. - /// - /// # - /// Weight: O(N) + O(D) where N is `MaxSelectedCandidates` bounded - /// by `MaxTopCandidates` and D the number of total delegators for - /// this collator bounded by `MaxCollatorsPerDelegator`. - /// bounded by `MaxUnstakeRequests`. - /// - Reads: [Origin Account], DelegatorState, BlockNumber, Unstaking, - /// Locks, TopCandidates, CandidatePool, MaxSelectedCandidates - /// - Writes: Unstaking, Locks, DelegatorState, CandidatePool, - /// TotalCollatorStake - /// # #[pallet::weight(::WeightInfo::delegator_stake_more( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get(), @@ -1914,12 +1536,12 @@ pub mod pallet { ensure!(!more.is_zero(), Error::::ValStakeZero); let candidate = T::Lookup::lookup(candidate)?; - let mut delegations = DelegatorState::::get(&delegator).ok_or(Error::::DelegatorNotFound)?; + let mut delegation = DelegatorState::::get(&delegator).ok_or(Error::::DelegatorNotFound)?; let mut collator = CandidatePool::::get(&candidate).ok_or(Error::::CandidateNotFound)?; ensure!(!collator.is_leaving(), Error::::CannotDelegateIfLeaving); - let stake_after = delegations + let stake_after = delegation .inc_delegation(candidate.clone(), more) - .ok_or(Error::::DelegationNotFound)?; + .map_err(|_| Error::::DelegationNotFound)?; // *** No Fail except during increase_lock beyond this point *** @@ -1952,7 +1574,7 @@ pub mod pallet { Self::do_inc_delegator_reward(&delegator, stake_after.saturating_sub(more), &candidate); CandidatePool::::insert(&candidate, collator); - DelegatorState::::insert(&delegator, delegations); + DelegatorState::::insert(&delegator, delegation); Self::deposit_event(Event::DelegatorStakedMore(delegator, candidate, before_total, after)); Ok(Some(::WeightInfo::delegator_stake_more( @@ -1979,14 +1601,6 @@ pub mod pallet { /// allowed range as set in the pallet's configuration. /// /// Emits `DelegatorStakedLess`. - /// - /// # - /// Weight: O(1) - /// - Reads: [Origin Account], DelegatorState, BlockNumber, Unstaking, - /// TopCandidates, CandidatePool, MaxSelectedCandidates - /// - Writes: Unstaking, DelegatorState, CandidatePool, - /// TotalCollatorStake - /// # #[pallet::weight(::WeightInfo::delegator_stake_less( T::MaxTopCandidates::get(), T::MaxDelegatorsPerCollator::get() @@ -2005,13 +1619,12 @@ pub mod pallet { ensure!(!collator.is_leaving(), Error::::CannotDelegateIfLeaving); let stake_after = delegations .dec_delegation(candidate.clone(), less) - .ok_or(Error::::DelegationNotFound)? + .map_err(|_| Error::::DelegationNotFound)? .ok_or(Error::::Underflow)?; - ensure!(stake_after >= T::MinDelegation::get(), Error::::DelegationBelowMin); ensure!( - delegations.total >= T::MinDelegatorStake::get(), - Error::::NomStakeBelowMin + stake_after >= T::MinDelegatorStake::get(), + Error::::DelegationBelowMin ); // *** No Fail except during prep_unstake beyond this point *** @@ -2142,25 +1755,19 @@ pub mod pallet { /// The dispatch origin must be a delegator. // TODO: Benchmark, unit tests #[pallet::weight(0)] - pub fn increment_delegator_rewards(origin: OriginFor) -> DispatchResultWithPostInfo { + pub fn increment_delegator_rewards(origin: OriginFor) -> DispatchResult { let delegator = ensure_signed(origin)?; - let state = DelegatorState::::get(&delegator).ok_or(Error::::DelegatorNotFound)?; + let delegation = DelegatorState::::get(&delegator).ok_or(Error::::DelegatorNotFound)?; + // should never throw + let collator = delegation.owner.ok_or(Error::::DelegationNotFound)?; // early exit let reward_count = RewardCount::::take(&delegator); ensure!(!reward_count.is_zero(), Error::::RewardsNotFound); - // iterate delegations - let mut post_weight: Weight = 0; - for delegation in state.delegations.into_iter() { - post_weight = post_weight.saturating_add(Self::do_inc_delegator_reward( - &delegator, - delegation.amount, - &delegation.owner, - )); - } + Self::do_inc_delegator_reward(&delegator, delegation.amount, &collator); - Ok(Some(post_weight).into()) + Ok(()) } /// Executes the annual reduction of the reward rates for collators and @@ -2227,11 +1834,6 @@ pub mod pallet { impl Pallet { /// Check whether an account is currently delegating. - /// - /// # - /// Weight: O(1) - /// - Reads: DelegatorState - /// # pub fn is_delegator(acc: &T::AccountId) -> bool { DelegatorState::::get(acc).is_some() } @@ -2240,11 +1842,6 @@ pub mod pallet { /// whether their state is CollatorStatus::Active. /// /// Returns Some(is_active) if the account is a candidate, else None. - /// - /// # - /// Weight: O(1) - /// - Reads: CandidatePool - /// # pub fn is_active_candidate(acc: &T::AccountId) -> Option { if let Some(state) = CandidatePool::::get(acc) { Some(state.status == CandidateStatus::Active) @@ -2258,12 +1855,6 @@ pub mod pallet { /// /// NOTE: It is assumed that the calling context checks whether the /// collator candidate is currently active before calling this function. - /// - /// # - /// Weight: O(1) - /// - Reads: TopCandidates, CandidatePool, TotalCollatorStake - /// - Writes: TopCandidates, TotalCollatorStake - /// # fn update_top_candidates( candidate: T::AccountId, old_self: BalanceOf, @@ -2391,14 +1982,6 @@ pub mod pallet { /// guarantee a single candidate's stake has changed, e.g. on genesis or /// when a collator leaves. Otherwise, please use /// [update_total_stake_by]. - /// - /// # - /// Weight: O(N) where N is `MaxSelectedCandidates` bounded by - /// `MaxTopCandidates` - /// - Reads: TopCandidates, MaxSelectedCandidates, N * CandidatePool, - /// TotalCollatorStake - /// - Writes: TotalCollatorStake - /// # fn update_total_stake() -> (u32, u32) { let mut num_of_delegators = 0u32; let mut collator_stake = BalanceOf::::zero(); @@ -2430,54 +2013,11 @@ pub mod pallet { (collators.len().saturated_into(), num_of_delegators) } - /// Update the delegator's state by removing the collator candidate from - /// the set of ongoing delegations. - /// - /// # - /// Weight: O(D) where D is the number of total delegators for - /// this collator bounded by `MaxCollatorsPerDelegator`. - /// - Reads: [Origin Account], DelegatorState, BlockNumber, Unstaking, - /// Locks, TopCandidates, D * CandidatePool, MaxSelectedCandidates - /// - Writes: Unstaking, Locks, DelegatorState, CandidatePool, - /// TotalCollatorStake - /// - Kills: DelegatorState if the delegator has not delegated to - /// another collator - /// # - fn delegator_revokes_collator(acc: T::AccountId, collator: T::AccountId) -> Result { - let mut delegator = DelegatorState::::get(&acc).ok_or(Error::::DelegatorNotFound)?; - let old_total = delegator.total; - let num_delegations: u32 = delegator.delegations.len().saturated_into::(); - let remaining = delegator - .rm_delegation(&collator) - .ok_or(Error::::DelegationNotFound)?; - - // remove delegation and increment rewards - if delegator.delegations.is_empty() { - // edge case; if no delegations remaining, leave set of delegators - Self::delegator_leaves_collator(acc.clone(), collator)?; - DelegatorState::::remove(&acc); - Self::deposit_event(Event::DelegatorLeft(acc, old_total)); - } else { - // can never fail iff MinDelegatorStake == MinDelegation - ensure!(remaining >= T::MinDelegatorStake::get(), Error::::NomStakeBelowMin); - Self::delegator_leaves_collator(acc.clone(), collator)?; - DelegatorState::::insert(&acc, delegator); - } - Ok(num_delegations) - } - /// Update the collator's state by removing the delegator's stake and /// starting the process to unlock the delegator's staked funds as well /// as incrementing their accumulated rewards. /// /// This operation affects the pallet's total stake. - /// - /// # - /// Weight: O(D) where D is the number of delegators for this - /// collator bounded by `MaxDelegatorsPerCollator`. - /// - Reads: CandidatePool, BlockNumber, Unstaking - /// - Writes: Unstaking, TotalCollatorStake, CandidatePool - /// # fn delegator_leaves_collator(delegator: T::AccountId, collator: T::AccountId) -> DispatchResult { let mut state = CandidatePool::::get(&collator).ok_or(Error::::CandidateNotFound)?; @@ -2527,61 +2067,11 @@ pub mod pallet { Ok(()) } - /// Check for remaining delegations of the delegator which has been - /// removed from the given collator. - /// - /// Returns the removed delegator's address and - /// * Either the updated delegator state if other delegations are still - /// remaining - /// * Or `None`, signalling the delegator state should be cleared once - /// the transaction cannot fail anymore. - fn prep_kick_delegator( - delegation: &StakeOf, - collator: &T::AccountId, - ) -> Result, DispatchError> { - let mut state = DelegatorState::::get(&delegation.owner).ok_or(Error::::DelegatorNotFound)?; - state.rm_delegation(collator); - - // we don't unlock immediately - Self::prep_unstake(&delegation.owner, delegation.amount, true)?; - - // return state if not empty for later removal after all checks have passed - if state.delegations.is_empty() { - Ok(ReplacedDelegator { - who: delegation.owner.clone(), - state: None, - }) - } else { - Ok(ReplacedDelegator { - who: delegation.owner.clone(), - state: Some(state), - }) - } - } - - /// Either clear the storage of a kicked delegator or update its - /// delegation state if it still contains other delegations. - fn update_kicked_delegator_storage(delegator: Option>) { - match delegator { - Some(ReplacedDelegator { - who, - state: Some(state), - }) => DelegatorState::::insert(who, state), - Some(ReplacedDelegator { who, .. }) => DelegatorState::::remove(who), - _ => (), - } - } - /// Return the best `MaxSelectedCandidates` many candidates. /// /// In case a collator from last round was replaced by a candidate with /// the same total stake during sorting, we revert this swap to /// prioritize collators over candidates. - /// - /// # - /// Weight: O(1) - /// - Reads: TopCandidates, MaxSelectedCandidates - /// # pub fn selected_candidates() -> BoundedVec { let candidates = TopCandidates::::get(); @@ -2615,23 +2105,11 @@ pub mod pallet { /// /// Emits `DelegationReplaced` if the stake exceeds one of the current /// delegations. - /// - /// # - /// Weight: O(D) where D is the number of delegators for this collator - /// bounded by `MaxDelegatorsPerCollator`. - /// - Reads/Writes: 0 - /// # #[allow(clippy::type_complexity)] fn do_update_delegator( stake: Stake>, mut state: Candidate, T::MaxDelegatorsPerCollator>, - ) -> Result< - ( - CandidateOf, - Option>, - ), - DispatchError, - > { + ) -> Result, DispatchError> { // attempt to replace the last element of the set let stake_to_remove = state .delegators @@ -2653,9 +2131,10 @@ pub mod pallet { // set rewards for kicked delegator Self::do_inc_delegator_reward(&stake_to_remove.owner, stake_to_remove.amount, &state.id); - - // update storage of kicked delegator - let kicked_delegator = Self::prep_kick_delegator(&stake_to_remove, &state.id)?; + // prepare unstaking for kicked delegator + Self::prep_unstake(&stake_to_remove.owner, stake_to_remove.amount, true)?; + // remove Delegator state for kicked delegator + DelegatorState::::remove(&stake_to_remove.owner); Self::deposit_event(Event::DelegationReplaced( stake.owner, @@ -2665,11 +2144,9 @@ pub mod pallet { state.id.clone(), state.total, )); - - Ok((state, Some(kicked_delegator))) - } else { - Ok((state, None)) } + + Ok(state) } /// Either set or increase the BalanceLock of target account to @@ -2677,13 +2154,6 @@ pub mod pallet { /// /// Consumes unstaked balance which can be unlocked in the future up to /// amount and updates `Unstaking` storage accordingly. - /// - /// # - /// Weight: O(U) where U is the number of locked unstaking requests - /// bounded by `MaxUnstakeRequests`. - /// - Reads: Unstaking, Locks - /// - Writes: Unstaking, Locks - /// # fn increase_lock(who: &T::AccountId, amount: BalanceOf, more: BalanceOf) -> Result { ensure!( pallet_balances::Pallet::::free_balance(who) >= amount.into(), @@ -2736,12 +2206,6 @@ pub mod pallet { /// Throws if the amount is zero (unlikely) or if active unlocking /// requests exceed limit. The latter defends against stake reduction /// spamming. - /// - /// # - /// Weight: O(1) - /// - Reads: BlockNumber, Unstaking - /// - Writes: Unstaking - /// # fn prep_unstake(who: &T::AccountId, amount: BalanceOf, is_removal: bool) -> DispatchResult { // should never occur but let's be safe ensure!(!amount.is_zero(), Error::::StakeNotFound); @@ -2775,16 +2239,6 @@ pub mod pallet { /// Clear the CandidatePool of the candidate and remove all delegations /// to the candidate. Moreover, prepare unstaking for the candidate and /// their former delegations. - /// - /// # - /// Weight: O(D + U) where D is the number of delegators of the collator - /// candidate bounded by `MaxDelegatorsPerCollator` and U is the - /// number of locked unstaking requests bounded by `MaxUnstakeRequests`. - /// - Reads: BlockNumber, D * DelegatorState, D * Unstaking - /// - Writes: D * DelegatorState, (D + 1) * Unstaking - /// - Kills: CandidatePool, DelegatorState for all delegators which only - /// delegated to the candidate - /// # fn remove_candidate( collator: &T::AccountId, state: &CandidateOf, @@ -2795,13 +2249,10 @@ pub mod pallet { Self::prep_unstake(&stake.owner, stake.amount, true)?; // remove delegation from delegator state if let Some(mut delegator) = DelegatorState::::get(&stake.owner) { - if let Some(remaining) = delegator.rm_delegation(collator) { - if remaining.is_zero() { - DelegatorState::::remove(&stake.owner); - } else { - DelegatorState::::insert(&stake.owner, delegator); - } - } + delegator + .rm_delegation(collator.clone()) + .map_err(|_| Error::::DelegationNotFound)?; + DelegatorState::::remove(&stake.owner); } } // prepare unstaking of collator candidate @@ -2833,14 +2284,6 @@ pub mod pallet { /// Withdraw all staked currency which was unstaked at least /// `StakeDuration` blocks ago. - /// - /// # - /// Weight: O(U) where U is the number of locked unstaking - /// requests bounded by `MaxUnstakeRequests`. - /// - Reads: Unstaking, Locks - /// - Writes: Unstaking, Locks - /// - Kills: Unstaking & Locks if no balance is locked anymore - /// # fn do_unlock(who: &T::AccountId) -> Result { let now = >::block_number(); let mut unstaking = >::get(who); @@ -2888,15 +2331,11 @@ pub mod pallet { /// Checks whether a delegator can still delegate in this round, e.g., /// if they have not delegated MaxDelegationsPerRound many times /// already in this round. - /// - /// # - /// Weight: O(1) - /// - Reads: LastDelegation, Round - /// # fn get_delegation_counter(delegator: &T::AccountId) -> Result { let last_delegation = >::get(delegator); let round = >::get(); + // reset counter if the round advanced since last delegation let counter = if last_delegation.round < round.current { 0u32 } else { @@ -2904,7 +2343,7 @@ pub mod pallet { }; ensure!( - T::MaxDelegationsPerRound::get() > counter, + counter < T::MaxDelegationsPerRound::get(), Error::::DelegationsPerRoundExceeded ); @@ -2931,12 +2370,6 @@ pub mod pallet { /// /// `col_reward_rate_per_block * col_max_stake * max_num_of_collators * /// NetworkRewardRate` - /// - /// # - /// Weight: O(1) - /// - Reads: InflationConfig, MaxCollatorCandidateStake, - /// MaxSelectedCandidates - /// # fn get_network_reward() -> NegativeImbalanceOf { // Multiplication with Perquintill cannot overflow let max_col_rewards = InflationConfig::::get().collator.reward_rate.per_block @@ -3057,7 +2490,7 @@ pub mod pallet { fn note_author(author: T::AccountId) { // should always include state except if the collator has been forcedly removed // via `force_remove_candidate` in the current or previous round - if CandidatePool::::get(author.clone()).is_some() { + if CandidatePool::::get(&author).is_some() { // necessary to compensate for a potentially fluctuating number of collators let authors = pallet_session::Pallet::::validators(); RewardCount::::mutate(&author, |count| { diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index cc2bb37f0a..206a157821 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -134,15 +134,13 @@ parameter_types! { pub const ExitQueueDelay: u32 = 2; pub const DefaultBlocksPerRound: BlockNumber = BLOCKS_PER_ROUND; pub const MinCollators: u32 = 2; + pub const MaxDelegationsPerRound: u32 = 2; #[derive(Debug, PartialEq)] pub const MaxDelegatorsPerCollator: u32 = 4; - #[derive(Debug, PartialEq)] - pub const MaxCollatorsPerDelegator: u32 = 4; pub const MinCollatorStake: Balance = 10; #[derive(Debug, PartialEq)] pub const MaxCollatorCandidates: u32 = 10; pub const MinDelegatorStake: Balance = 5; - pub const MinDelegation: Balance = 3; pub const MaxUnstakeRequests: u32 = 6; pub const NetworkRewardRate: Perquintill = Perquintill::from_percent(10); pub const NetworkRewardStart: BlockNumber = 5 * 5 * 60 * 24 * 36525 / 100; @@ -166,14 +164,12 @@ impl Config for Test { type ExitQueueDelay = ExitQueueDelay; type MinCollators = MinCollators; type MinRequiredCollators = MinCollators; - type MaxDelegationsPerRound = MaxDelegatorsPerCollator; + type MaxDelegationsPerRound = MaxDelegationsPerRound; type MaxDelegatorsPerCollator = MaxDelegatorsPerCollator; - type MaxCollatorsPerDelegator = MaxCollatorsPerDelegator; type MinCollatorStake = MinCollatorStake; type MinCollatorCandidateStake = MinCollatorStake; type MaxTopCandidates = MaxCollatorCandidates; type MinDelegatorStake = MinDelegatorStake; - type MinDelegation = MinDelegation; type MaxUnstakeRequests = MaxUnstakeRequests; type NetworkRewardRate = NetworkRewardRate; type NetworkRewardStart = NetworkRewardStart; diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index bef86d0807..9a713081e7 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -24,6 +24,7 @@ use frame_support::{ assert_noop, assert_ok, storage::bounded_btree_map::BoundedBTreeMap, traits::EstimateNextSessionRotation, BoundedVec, }; +use pallet_authorship::EventHandler; use pallet_balances::{BalanceLock, Error as BalancesError, Reasons}; use pallet_session::{SessionManager, ShouldEndSession}; use sp_runtime::{traits::Zero, Perbill, Permill, Perquintill, SaturatedConversion}; @@ -348,6 +349,7 @@ fn collator_exit_executes_after_delay() { (7, 100), (8, 9), (9, 4), + (10, 10), ]) .with_collators(vec![(1, 500), (2, 200), (7, 100)]) .with_delegators(vec![(3, 1, 100), (4, 1, 100), (5, 2, 100), (6, 2, 100)]) @@ -381,7 +383,7 @@ fn collator_exit_executes_after_delay() { // Still three, candidate didn't leave yet assert_eq!(CandidatePool::::count(), 3); assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(3), 2, 10), + StakePallet::join_delegators(Origin::signed(10), 2, 10), Error::::CannotDelegateIfLeaving ); assert_eq!(StakePallet::selected_candidates().into_inner(), vec![1, 7]); @@ -712,47 +714,31 @@ fn execute_leave_candidates_with_delay() { } assert_eq!( StakePallet::delegator_state(11), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 1, amount: 110 }].try_into().unwrap() - ), - total: 110 - } - ) + Some(Delegator:: { + owner: Some(1), + amount: 110, + }) ); assert_eq!( StakePallet::delegator_state(12), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 1, amount: 120 }].try_into().unwrap() - ), - total: 120 - } - ) + Some(Delegator:: { + owner: Some(1), + amount: 120 + }) ); assert_eq!( StakePallet::delegator_state(13), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 2, amount: 130 }].try_into().unwrap() - ), - total: 130 - } - ) + Some(Delegator:: { + owner: Some(2), + amount: 130 + }) ); assert_eq!( StakePallet::delegator_state(14), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 2, amount: 140 }].try_into().unwrap() - ), - total: 140 - } - ) + Some(Delegator:: { + owner: Some(2), + amount: 140 + }) ); for delegator in 11u64..=14u64 { assert!(StakePallet::is_delegator(&delegator)); @@ -829,47 +815,31 @@ fn execute_leave_candidates_with_delay() { } assert_eq!( StakePallet::delegator_state(11), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 1, amount: 110 }].try_into().unwrap() - ), - total: 110 - } - ) + Some(Delegator:: { + owner: Some(1), + amount: 110 + }) ); assert_eq!( StakePallet::delegator_state(12), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 1, amount: 120 }].try_into().unwrap() - ), - total: 120 - } - ) + Some(Delegator:: { + owner: Some(1), + amount: 120 + }) ); assert_eq!( StakePallet::delegator_state(13), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 2, amount: 130 }].try_into().unwrap() - ), - total: 130 - } - ) + Some(Delegator:: { + owner: Some(2), + amount: 130 + }) ); assert_eq!( StakePallet::delegator_state(14), - Some( - Delegator::::MaxCollatorsPerDelegator> { - delegations: OrderedSet::from( - vec![StakeOf:: { owner: 2, amount: 140 }].try_into().unwrap() - ), - total: 140 - } - ) + Some(Delegator:: { + owner: Some(2), + amount: 140 + }) ); for delegator in 11u64..=14u64 { assert!(StakePallet::is_delegator(&delegator)); @@ -912,6 +882,7 @@ fn execute_leave_candidates_with_delay() { }); } +// FIXME: Re-enable or potentially remove entirely #[test] fn multiple_delegations() { ExtBuilder::default() @@ -928,6 +899,14 @@ fn multiple_delegations() { (10, 100), (11, 100), (12, 100), + // new + (13, 100), + (14, 100), + (15, 100), + (16, 100), + (17, 100), + (18, 100), + (99, 1), ]) .with_collators(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) .with_delegators(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) @@ -935,58 +914,64 @@ fn multiple_delegations() { .build() .execute_with(|| { assert_ok!(StakePallet::set_max_selected_candidates(Origin::root(), 5)); - roll_to(8, vec![]); + roll_to( + 8, + vec![Some(1), Some(2), Some(3), Some(4), Some(5), Some(1), Some(2), Some(3)], + ); // chooses top MaxSelectedCandidates (5), in order assert_eq!(StakePallet::selected_candidates().into_inner(), vec![1, 2, 3, 4, 5]); let mut expected = vec![Event::MaxSelectedCandidatesSet(2, 5), Event::NewRound(5, 1)]; assert_eq!(events(), expected); assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(6), 1, 10), - Error::::AlreadyDelegatedCollator, - ); - assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(6), 2, 2), + StakePallet::join_delegators(Origin::signed(13), 2, 2), Error::::DelegationBelowMin, ); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 2, 10)); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 4, 10)); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 3, 10)); + assert_ok!(StakePallet::join_delegators(Origin::signed(13), 2, 10)); + assert_ok!(StakePallet::join_delegators(Origin::signed(14), 4, 10)); + assert_ok!(StakePallet::join_delegators(Origin::signed(15), 3, 10)); assert_eq!(StakePallet::selected_candidates().into_inner(), vec![1, 2, 4, 3, 5]); assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(6), 5, 10), - Error::::MaxCollatorsPerDelegatorExceeded, + StakePallet::join_delegators(Origin::signed(6), 5, 10), + Error::::AlreadyDelegating, ); - roll_to(16, vec![]); + roll_to( + 16, + vec![Some(1), Some(2), Some(3), Some(4), Some(5), Some(1), Some(2), Some(3)], + ); assert_eq!(StakePallet::selected_candidates().into_inner(), vec![1, 2, 4, 3, 5]); let mut new = vec![ - Event::Delegation(6, 10, 2, 50), - Event::Delegation(6, 10, 4, 30), - Event::Delegation(6, 10, 3, 30), + Event::Delegation(13, 10, 2, 50), + Event::Delegation(14, 10, 4, 30), + Event::Delegation(15, 10, 3, 30), Event::NewRound(10, 2), Event::NewRound(15, 3), ]; expected.append(&mut new); assert_eq!(events(), expected); - roll_to(21, vec![]); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(7), 2, 80)); + roll_to(21, vec![Some(1), Some(2), Some(3), Some(4), Some(5)]); + assert_ok!(StakePallet::join_delegators(Origin::signed(16), 2, 80)); assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(7), 3, 11), + StakePallet::join_delegators(Origin::signed(99), 3, 11), BalancesError::::InsufficientBalance ); assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(10), 2, 10), + StakePallet::join_delegators(Origin::signed(17), 2, 10), Error::::TooManyDelegators ); - // kick 6 - assert!(StakePallet::unstaking(6).is_empty()); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(10), 2, 11)); - assert!(StakePallet::delegator_state(6).is_some()); - assert_eq!(StakePallet::unstaking(6).get(&23), Some(&10u128)); - // kick 9 + // kick 13 by staking 1 more (11 > 10) + assert!(StakePallet::unstaking(13).is_empty()); + assert_ok!(StakePallet::join_delegators(Origin::signed(17), 2, 11)); + assert!(StakePallet::delegator_state(13).is_none()); + assert_eq!(StakePallet::unstaking(13).get(&23), Some(&10u128)); + // kick 9 by staking 1 more (11 > 10) assert!(StakePallet::unstaking(9).is_empty()); + assert!(StakePallet::rewards(9).is_zero()); assert_ok!(StakePallet::join_delegators(Origin::signed(11), 2, 11)); + // 11 should be initiated with the same reward counter as their collator 2 + assert_eq!(StakePallet::reward_count(2), StakePallet::reward_count(11)); + assert!(StakePallet::delegator_state(9).is_none()); assert_eq!(StakePallet::unstaking(9).get(&23), Some(&10u128)); assert!(!StakePallet::candidate_pool(2) @@ -994,13 +979,13 @@ fn multiple_delegations() { .delegators .contains(&StakeOf:: { owner: 9, amount: 10 })); - roll_to(26, vec![]); + roll_to(26, vec![Some(1), Some(2), Some(3), Some(4), Some(5)]); assert_eq!(StakePallet::selected_candidates().into_inner(), vec![2, 1, 4, 3, 5]); let mut new2 = vec![ Event::NewRound(20, 4), - Event::Delegation(7, 80, 2, 130), - Event::DelegationReplaced(10, 11, 6, 10, 2, 131), - Event::Delegation(10, 11, 2, 131), + Event::Delegation(16, 80, 2, 130), + Event::DelegationReplaced(17, 11, 13, 10, 2, 131), + Event::Delegation(17, 11, 2, 131), Event::DelegationReplaced(11, 11, 9, 10, 2, 132), Event::Delegation(11, 11, 2, 132), Event::NewRound(25, 5), @@ -1014,7 +999,7 @@ fn multiple_delegations() { MetaEvent::StakePallet(Event::CollatorScheduledExit(5, 2, 7)) ); - roll_to(31, vec![]); + roll_to(31, vec![Some(1), Some(2), Some(3), Some(4), Some(5)]); let mut new3 = vec![ Event::LeftTopCandidates(2), Event::CollatorScheduledExit(5, 2, 7), @@ -1024,53 +1009,53 @@ fn multiple_delegations() { assert_eq!(events(), expected); // test join_delegator errors - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(8), 1, 10)); + assert_ok!(StakePallet::join_delegators(Origin::signed(18), 1, 10)); assert_noop!( StakePallet::join_delegators(Origin::signed(12), 1, 10), Error::::TooManyDelegators ); - assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(12), 1, 10), - Error::::NotYetDelegating - ); assert_ok!(StakePallet::join_delegators(Origin::signed(12), 1, 11)); // verify that delegations are removed after collator leaves, not before - assert_eq!(StakePallet::delegator_state(7).unwrap().total, 90); - assert_eq!(StakePallet::delegator_state(7).unwrap().delegations.len(), 2usize); - assert_eq!(StakePallet::delegator_state(11).unwrap().total, 11); - assert_eq!(StakePallet::delegator_state(11).unwrap().delegations.len(), 1usize); - // 6 already has 10 in - assert_eq!(Balances::usable_balance(&7), 10); - assert_eq!(Balances::usable_balance(&11), 89); - assert_eq!(Balances::free_balance(&7), 100); - assert_eq!(Balances::free_balance(&11), 100); - - roll_to(35, vec![]); + assert!(StakePallet::candidate_pool(2) + .unwrap() + .delegators + .contains(&StakeOf:: { owner: 8, amount: 10 })); + assert!(StakePallet::candidate_pool(2) + .unwrap() + .delegators + .contains(&StakeOf:: { owner: 17, amount: 11 })); + assert_eq!(StakePallet::delegator_state(8).unwrap().amount, 10); + assert_eq!(StakePallet::delegator_state(17).unwrap().amount, 11); + assert_eq!(Balances::usable_balance(&8), 90); + assert_eq!(Balances::usable_balance(&17), 89); + assert_eq!(Balances::free_balance(&8), 100); + assert_eq!(Balances::free_balance(&17), 100); + + roll_to(35, vec![Some(1), Some(2), Some(3), Some(4)]); assert_ok!(StakePallet::execute_leave_candidates(Origin::signed(2), 2)); - let mut unbonding_7: BoundedBTreeMap, ::MaxUnstakeRequests> = + let mut unbonding_8: BoundedBTreeMap, ::MaxUnstakeRequests> = BoundedBTreeMap::new(); - assert_ok!(unbonding_7.try_insert(35u64 + ::StakeDuration::get() as u64, 80)); - assert_eq!(StakePallet::unstaking(7), unbonding_7); - let mut unbonding_11: BoundedBTreeMap, ::MaxUnstakeRequests> = + assert_ok!(unbonding_8.try_insert(35u64 + ::StakeDuration::get() as u64, 10)); + assert_eq!(StakePallet::unstaking(8), unbonding_8); + let mut unbonding_17: BoundedBTreeMap, ::MaxUnstakeRequests> = BoundedBTreeMap::new(); - assert_ok!(unbonding_11.try_insert(35u64 + ::StakeDuration::get() as u64, 11)); - assert_eq!(StakePallet::unstaking(11), unbonding_11); - - roll_to(37, vec![]); - assert_eq!(StakePallet::delegator_state(7).unwrap().total, 10); - assert!(StakePallet::delegator_state(11).is_none()); - assert_eq!(StakePallet::delegator_state(7).unwrap().delegations.len(), 1usize); - assert_ok!(StakePallet::unlock_unstaked(Origin::signed(7), 7)); - assert_ok!(StakePallet::unlock_unstaked(Origin::signed(11), 11)); + assert_ok!(unbonding_17.try_insert(35u64 + ::StakeDuration::get() as u64, 11)); + assert_eq!(StakePallet::unstaking(17), unbonding_17); + + roll_to(37, vec![Some(1), Some(2)]); + assert!(StakePallet::delegator_state(8).is_none()); + assert!(StakePallet::delegator_state(17).is_none()); + assert_ok!(StakePallet::unlock_unstaked(Origin::signed(8), 8)); + assert_ok!(StakePallet::unlock_unstaked(Origin::signed(17), 17)); assert_noop!( StakePallet::unlock_unstaked(Origin::signed(12), 12), Error::::UnstakingIsEmpty ); - assert_eq!(Balances::usable_balance(&11), 100); - assert_eq!(Balances::usable_balance(&7), 90); - assert_eq!(Balances::free_balance(&11), 100); - assert_eq!(Balances::free_balance(&7), 100); + assert_eq!(Balances::usable_balance(&17), 100); + assert_eq!(Balances::usable_balance(&8), 100); + assert_eq!(Balances::free_balance(&17), 100); + assert_eq!(Balances::free_balance(&8), 100); }); } @@ -1161,29 +1146,19 @@ fn should_update_total_stake() { ); old_stake = StakePallet::total_collator_stake(); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(11), 2, 150)); - assert_eq!( - StakePallet::total_collator_stake(), - TotalStake { - delegators: old_stake.delegators + 150, - ..old_stake - } - ); - - old_stake = StakePallet::total_collator_stake(); - assert_eq!(StakePallet::delegator_state(11).unwrap().total, 350); + assert_eq!(StakePallet::delegator_state(11).unwrap().amount, 200); assert_ok!(StakePallet::leave_delegators(Origin::signed(11))); assert_eq!( StakePallet::total_collator_stake(), TotalStake { - delegators: old_stake.delegators - 350, + delegators: old_stake.delegators - 200, ..old_stake } ); let old_stake = StakePallet::total_collator_stake(); - assert_eq!(StakePallet::delegator_state(8).unwrap().total, 10); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(8), 2)); + assert_eq!(StakePallet::delegator_state(8).unwrap().amount, 10); + assert_ok!(StakePallet::leave_delegators(Origin::signed(8))); assert_eq!( StakePallet::total_collator_stake(), TotalStake { @@ -1250,10 +1225,6 @@ fn collators_bond() { StakePallet::candidate_stake_less(Origin::signed(6), 50), Error::::CandidateNotFound ); - assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(6), 50, 10), - Error::::CandidateNotFound - ); assert_ok!(StakePallet::candidate_stake_more(Origin::signed(1), 50)); assert_noop!( StakePallet::candidate_stake_more(Origin::signed(1), 40), @@ -1365,10 +1336,6 @@ fn delegators_bond() { StakePallet::delegator_stake_less(Origin::signed(6), 1, 8), Error::::DelegationBelowMin ); - assert_noop!( - StakePallet::delegator_stake_less(Origin::signed(6), 1, 6), - Error::::NomStakeBelowMin - ); assert_ok!(StakePallet::delegator_stake_more(Origin::signed(6), 1, 10)); assert_noop!( StakePallet::delegator_stake_less(Origin::signed(6), 2, 5), @@ -1380,7 +1347,7 @@ fn delegators_bond() { ); assert_noop!( StakePallet::join_delegators(Origin::signed(10), 1, 4), - Error::::NomStakeBelowMin + Error::::DelegationBelowMin ); roll_to(9, vec![]); @@ -1400,55 +1367,27 @@ fn delegators_bond() { } #[test] -fn revoke_delegation_or_leave_delegators() { +fn should_leave_delegators() { ExtBuilder::default() - .with_balances(vec![ - (1, 100), - (2, 100), - (3, 100), - (4, 100), - (5, 100), - (6, 100), - (7, 100), - (8, 100), - (9, 100), - (10, 100), - ]) - .with_collators(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) - .with_delegators(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) - .set_blocks_per_round(5) + .with_balances(vec![(1, 100), (2, 100)]) + .with_collators(vec![(1, 100)]) + .with_delegators(vec![(2, 1, 100)]) .build() .execute_with(|| { - roll_to(4, vec![]); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); + assert!(StakePallet::delegator_state(2).is_none()); + assert!(!StakePallet::candidate_pool(1) + .unwrap() + .delegators + .contains(&StakeOf:: { owner: 2, amount: 100 })); assert_noop!( - StakePallet::revoke_delegation(Origin::signed(1), 2), + StakePallet::leave_delegators(Origin::signed(2)), Error::::DelegatorNotFound ); - assert_noop!( - StakePallet::revoke_delegation(Origin::signed(6), 2), - Error::::DelegationNotFound - ); assert_noop!( StakePallet::leave_delegators(Origin::signed(1)), Error::::DelegatorNotFound ); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 2, 3)); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 3, 3)); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(6), 1)); - // cannot revoke delegation because would leave remaining total below - // MinDelegatorStake - assert_noop!( - StakePallet::revoke_delegation(Origin::signed(6), 2), - Error::::NomStakeBelowMin - ); - assert_noop!( - StakePallet::revoke_delegation(Origin::signed(6), 3), - Error::::NomStakeBelowMin - ); - // can revoke both remaining by calling leave delegators - assert_ok!(StakePallet::leave_delegators(Origin::signed(6))); - // this leads to 8 leaving set of delegators - assert_ok!(StakePallet::revoke_delegation(Origin::signed(8), 2)); }); } @@ -1659,7 +1598,7 @@ fn coinbase_rewards_few_blocks_detailed_check() { assert_eq!(Balances::usable_balance(&3), user_3 + d_rewards / 2 * 3); assert_eq!(Balances::usable_balance(&4), user_4 + d_rewards / 4 * 3); assert_eq!(Balances::usable_balance(&5), user_5 + d_rewards / 4); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(5), 2)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(5))); // 2 is block author for 5th block roll_to_claim_rewards(6, authors); @@ -1682,7 +1621,7 @@ fn delegator_should_not_receive_rewards_after_revoking() { .with_inflation(10, 15, 40, 15, 5) .build() .execute_with(|| { - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); let authors: Vec> = (1u64..100u64).map(|_| Some(1u64)).collect(); assert_eq!(Balances::usable_balance(&1), Balance::zero()); assert_eq!(Balances::usable_balance(&2), Balance::zero()); @@ -1703,7 +1642,7 @@ fn delegator_should_not_receive_rewards_after_revoking() { .with_inflation(10, 15, 40, 15, 5) .build() .execute_with(|| { - assert_ok!(StakePallet::revoke_delegation(Origin::signed(3), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(3))); let authors: Vec> = (1u64..100u64).map(|_| Some(1u64)).collect(); assert_eq!(Balances::usable_balance(&1), Balance::zero()); assert_eq!(Balances::usable_balance(&2), Balance::zero()); @@ -2256,7 +2195,7 @@ fn unlock_unstaked() { .with_delegators(vec![(2, 1, 100)]) .build() .execute_with(|| { - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); let mut unstaking: BoundedBTreeMap, ::MaxUnstakeRequests> = BoundedBTreeMap::new(); assert_ok!(unstaking.try_insert(3, 100)); @@ -2275,7 +2214,7 @@ fn unlock_unstaked() { // join delegators and revoke again --> consume unstaking at block 3 roll_to(2, vec![]); assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 100)); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); unstaking.remove(&3); assert_ok!(unstaking.try_insert(4, 100)); assert_eq!(StakePallet::unstaking(2), unstaking); @@ -2314,7 +2253,7 @@ fn unlock_unstaked() { .with_delegators(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); let mut unstaking: BoundedBTreeMap, ::MaxUnstakeRequests> = BoundedBTreeMap::new(); assert_ok!(unstaking.try_insert(3, 10)); @@ -2333,7 +2272,7 @@ fn unlock_unstaked() { // join delegators and revoke again roll_to(2, vec![]); assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 100)); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); unstaking.remove(&3); assert_ok!(unstaking.try_insert(4, 100)); lock.amount = 100; @@ -2374,7 +2313,7 @@ fn unlock_unstaked() { .with_delegators(vec![(2, 1, 100)]) .build() .execute_with(|| { - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); let mut unstaking: BoundedBTreeMap, ::MaxUnstakeRequests> = BoundedBTreeMap::new(); assert_ok!(unstaking.try_insert(3, 100)); @@ -2393,7 +2332,7 @@ fn unlock_unstaked() { // join delegators and revoke again roll_to(2, vec![]); assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 10)); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(2), 1)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); assert_ok!(unstaking.try_insert(3, 90)); assert_ok!(unstaking.try_insert(4, 10)); assert_eq!(StakePallet::unstaking(2), unstaking); @@ -2968,60 +2907,49 @@ fn decrease_max_candidate_stake() { #[test] fn exceed_delegations_per_round() { ExtBuilder::default() - .with_balances(vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100)]) - .with_collators(vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)]) - .with_delegators(vec![(6, 1, 10)]) + .with_balances(vec![(1, 100), (2, 100)]) + .with_collators(vec![(1, 100)]) + .with_delegators(vec![(2, 1, 100)]) .build() .execute_with(|| { - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 2, 10)); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 3, 10)); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(6), 4, 10)); - assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(6), 5, 10), - Error::::MaxCollatorsPerDelegatorExceeded - ); - - // revoke delegation to allow one more collator for this delegator - assert_ok!(StakePallet::revoke_delegation(Origin::signed(6), 4)); + // leave and re-join to set counter to 2 (= MaxDelegationsPerRound) + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); + assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 100)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); // reached max delegations in this round assert_noop!( - StakePallet::delegate_another_candidate(Origin::signed(6), 5, 10), - Error::::DelegationsPerRoundExceeded - ); - - // revoke all delegations in the same round - assert_ok!(StakePallet::leave_delegators(Origin::signed(6))); - assert_noop!( - StakePallet::join_delegators(Origin::signed(6), 1, 10), + StakePallet::join_delegators(Origin::signed(2), 1, 100), Error::::DelegationsPerRoundExceeded ); // roll to next round to clear DelegationCounter roll_to(5, vec![]); assert_eq!( - StakePallet::last_delegation(6), - DelegationCounter { round: 0, counter: 4 } + StakePallet::last_delegation(2), + DelegationCounter { round: 0, counter: 2 } ); - assert_ok!(StakePallet::join_delegators(Origin::signed(6), 1, 10),); + assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 100)); + // counter should be reset because the round changed assert_eq!( - StakePallet::last_delegation(6), + StakePallet::last_delegation(2), DelegationCounter { round: 1, counter: 1 } ); - assert_ok!(StakePallet::leave_delegators(Origin::signed(6))); - assert_ok!(StakePallet::join_delegators(Origin::signed(6), 1, 10),); - assert_ok!(StakePallet::leave_delegators(Origin::signed(6))); - assert_ok!(StakePallet::join_delegators(Origin::signed(6), 1, 10),); - assert_ok!(StakePallet::leave_delegators(Origin::signed(6))); - assert_ok!(StakePallet::join_delegators(Origin::signed(6), 1, 10),); - assert_ok!(StakePallet::leave_delegators(Origin::signed(6))); - assert_eq!( - StakePallet::last_delegation(6), - DelegationCounter { round: 1, counter: 4 } + // leave and re-join to set counter to 2 (= MaxDelegationsPerRound)) + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); + assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 100)); + assert_noop!( + StakePallet::join_delegators(Origin::signed(2), 1, 100), + Error::::AlreadyDelegating ); + assert_ok!(StakePallet::leave_delegators(Origin::signed(2))); assert_noop!( - StakePallet::join_delegators(Origin::signed(6), 1, 10), + StakePallet::join_delegators(Origin::signed(2), 1, 100), Error::::DelegationsPerRoundExceeded ); + assert_eq!( + StakePallet::last_delegation(2), + DelegationCounter { round: 1, counter: 2 } + ); }); } @@ -3034,7 +2962,7 @@ fn force_remove_candidate() { .build() .execute_with(|| { assert_eq!(CandidatePool::::count(), 3); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(4), 2, 50)); + assert_ok!(StakePallet::join_delegators(Origin::signed(6), 2, 50)); assert_eq!(StakePallet::selected_candidates().into_inner(), vec![1, 2]); assert!(StakePallet::unstaking(1).get(&3).is_none()); assert!(StakePallet::unstaking(2).get(&3).is_none()); @@ -3065,14 +2993,8 @@ fn force_remove_candidate() { assert_eq!(StakePallet::selected_candidates().into_inner(), vec![2, 3]); assert_eq!(CandidatePool::::count(), 2); assert!(StakePallet::candidate_pool(1).is_none()); + assert!(StakePallet::delegator_state(4).is_none()); assert!(StakePallet::delegator_state(5).is_none()); - assert_eq!( - StakePallet::delegator_state(4), - Some(Delegator { - delegations: OrderedSet::from(vec![StakeOf:: { owner: 2, amount: 50 }].try_into().unwrap()), - total: 50 - }) - ); assert_eq!(StakePallet::unstaking(1).get(&3), Some(&100)); assert_eq!(StakePallet::unstaking(4).get(&3), Some(&50)); assert_eq!(StakePallet::unstaking(5).get(&3), Some(&50)); @@ -3264,7 +3186,7 @@ fn prioritize_collators() { .unwrap() ) ); - assert_ok!(StakePallet::revoke_delegation(Origin::signed(7), 5)); + assert_ok!(StakePallet::leave_delegators(Origin::signed(7))); assert_eq!(StakePallet::selected_candidates().into_inner(), vec![3, 5]); assert_eq!( StakePallet::top_candidates(), @@ -3294,9 +3216,11 @@ fn prioritize_delegators() { (5, 1000), (6, 1000), (7, 1000), + (8, 1000), + (9, 1000), ]) .with_collators(vec![(1, 100), (2, 100), (3, 100)]) - .with_delegators(vec![(5, 1, 100), (4, 2, 100), (7, 2, 100), (6, 2, 100)]) + .with_delegators(vec![(4, 2, 100), (7, 2, 100), (6, 2, 100)]) .build() .execute_with(|| { assert_eq!(StakePallet::selected_candidates().into_inner(), vec![2, 1]); @@ -3312,7 +3236,7 @@ fn prioritize_delegators() { .unwrap() ) ); - assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(5), 2, 110)); + assert_ok!(StakePallet::join_delegators(Origin::signed(5), 2, 110)); assert_eq!( StakePallet::candidate_pool(2).unwrap().delegators, OrderedSet::from_sorted_set( @@ -3326,17 +3250,6 @@ fn prioritize_delegators() { .unwrap() ) ); - assert_eq!( - StakePallet::delegator_state(5).unwrap().delegations, - OrderedSet::from_sorted_set( - vec![ - StakeOf:: { owner: 2, amount: 110 }, - StakeOf:: { owner: 1, amount: 100 } - ] - .try_into() - .unwrap() - ) - ); // delegate_less assert_ok!(StakePallet::delegator_stake_less(Origin::signed(5), 2, 10)); @@ -3353,17 +3266,6 @@ fn prioritize_delegators() { .unwrap() ) ); - assert_eq!( - StakePallet::delegator_state(5).unwrap().delegations, - OrderedSet::from_sorted_set( - vec![ - StakeOf:: { owner: 2, amount: 100 }, - StakeOf:: { owner: 1, amount: 100 } - ] - .try_into() - .unwrap() - ) - ); // delegate_more assert_ok!(StakePallet::delegator_stake_more(Origin::signed(6), 2, 10)); @@ -3933,3 +3835,290 @@ fn update_total_stake_no_collator_changes() { ); }); } + +#[test] +fn rewards_candidate_stake_more() { + ExtBuilder::default() + .with_balances(vec![(1, 2 * DECIMALS), (2, DECIMALS), (3, DECIMALS)]) + .with_collators(vec![(1, DECIMALS)]) + .with_delegators(vec![(2, 1, DECIMALS), (3, 1, DECIMALS)]) + .build() + .execute_with(|| { + // note once to set counter to 1 + StakePallet::note_author(1); + assert_eq!(StakePallet::reward_count(1), 1); + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::reward_count(3).is_zero()); + (1..=3).for_each(|id| { + assert!(StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); + }); + + // stake more to trigger reward incrementing + assert_ok!(StakePallet::candidate_stake_more(Origin::signed(1), DECIMALS)); + (1..=3).for_each(|id| { + assert!(!StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); + assert!(StakePallet::reward_count(id).is_zero(), "acc_id {:?}", id); + }); + }); +} + +#[test] +fn rewards_candidate_stake_less() { + ExtBuilder::default() + .with_balances(vec![(1, 2 * DECIMALS), (2, DECIMALS), (3, DECIMALS)]) + .with_collators(vec![(1, 2 * DECIMALS)]) + .with_delegators(vec![(2, 1, DECIMALS), (3, 1, DECIMALS)]) + .build() + .execute_with(|| { + // note once to set counter to 1 + StakePallet::note_author(1); + assert_eq!(StakePallet::reward_count(1), 1); + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::reward_count(3).is_zero()); + (1..=3).for_each(|id| { + assert!(StakePallet::rewards(id).is_zero()); + }); + + // stake more to trigger reward incrementing + assert_ok!(StakePallet::candidate_stake_less(Origin::signed(1), DECIMALS)); + (1..=3).for_each(|id| { + assert!(!StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); + assert!(StakePallet::reward_count(id).is_zero(), "acc_id {:?}", id); + }); + }); +} +#[test] +fn rewards_candidate_leave_network() { + ExtBuilder::default() + .with_balances(vec![ + (1, 2 * DECIMALS), + (2, DECIMALS), + (3, DECIMALS), + (4, DECIMALS), + (5, DECIMALS), + ]) + .with_collators(vec![(1, 2 * DECIMALS), (4, DECIMALS), (5, DECIMALS)]) + .with_delegators(vec![(2, 1, DECIMALS), (3, 1, DECIMALS)]) + .build() + .execute_with(|| { + // init does not increment rewards + assert_ok!(StakePallet::init_leave_candidates(Origin::signed(1))); + + // advance two rounds to enable leaving + roll_to( + 10, + vec![ + // we're in block 1 already, so cannot note_author in roll_to + None, + Some(1), + Some(2), + Some(1), + Some(2), + Some(1), + Some(2), + Some(1), + Some(2), + ], + ); + assert_eq!(StakePallet::reward_count(1), 4 * 2); + + // count for delegators should not be incremented + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::reward_count(3).is_zero()); + + // rewards should not be incremented + (1..=3).for_each(|id| { + assert!(StakePallet::rewards(id).is_zero()); + }); + + // execute leave intent to trigger reward incrementing + assert_ok!(StakePallet::execute_leave_candidates(Origin::signed(1), 1)); + (1..=3).for_each(|id| { + assert!(!StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); + assert!(StakePallet::reward_count(id).is_zero(), "acc_id {:?}", id); + }); + }); +} + +#[test] +fn reward_count_join_delegators() { + ExtBuilder::default() + .with_balances(vec![(1, 100), (2, 100)]) + .with_collators(vec![(1, 100)]) + .build() + .execute_with(|| { + // note once to set counter to 1 + StakePallet::note_author(1); + assert_eq!(StakePallet::reward_count(1), 1); + assert_ok!(StakePallet::join_delegators(Origin::signed(2), 1, 100)); + // delegator should have same counter as collator upon joining + assert_eq!(StakePallet::reward_count(2), 1); + }); +} + +#[test] +fn rewards_delegator_stake_more() { + ExtBuilder::default() + .with_balances(vec![(1, DECIMALS), (2, DECIMALS), (3, 2 * DECIMALS)]) + .with_collators(vec![(1, DECIMALS)]) + .with_delegators(vec![(2, 1, DECIMALS), (3, 1, DECIMALS)]) + .build() + .execute_with(|| { + // note once to set counter to 1 + StakePallet::note_author(1); + assert_eq!(StakePallet::reward_count(1), 1); + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::reward_count(3).is_zero()); + (1..=3).for_each(|id| { + assert!(StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); + }); + + // stake more to trigger reward incrementing just for 3 + assert_ok!(StakePallet::delegator_stake_more(Origin::signed(3), 1, DECIMALS)); + // 1 should still have counter 1 but no rewards + assert_eq!(StakePallet::reward_count(1), 1); + assert!(StakePallet::rewards(1).is_zero()); + // 2 should still have neither rewards nor counter + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::rewards(2).is_zero()); + // 3 should have rewards and the same counter as 1 + assert_eq!(StakePallet::reward_count(3), 1); + assert!(!StakePallet::rewards(3).is_zero()); + }); +} + +#[test] +fn rewards_delegator_stake_less() { + ExtBuilder::default() + .with_balances(vec![(1, DECIMALS), (2, DECIMALS), (3, 2 * DECIMALS)]) + .with_collators(vec![(1, DECIMALS)]) + .with_delegators(vec![(2, 1, DECIMALS), (3, 1, 2 * DECIMALS)]) + .build() + .execute_with(|| { + // note once to set counter to 1 + StakePallet::note_author(1); + assert_eq!(StakePallet::reward_count(1), 1); + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::reward_count(3).is_zero()); + (1..=3).for_each(|id| { + assert!(StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); + }); + + // stake more to trigger reward incrementing just for 3 + assert_ok!(StakePallet::delegator_stake_less(Origin::signed(3), 1, DECIMALS)); + // 1 should still have counter 1 but no rewards + assert_eq!(StakePallet::reward_count(1), 1); + assert!(StakePallet::rewards(1).is_zero()); + // 2 should still have neither rewards nor counter + assert!(StakePallet::reward_count(2).is_zero()); + assert!(StakePallet::rewards(2).is_zero()); + // 3 should have rewards and the same counter as 1 + assert_eq!(StakePallet::reward_count(3), 1); + assert!(!StakePallet::rewards(3).is_zero()); + }); +} + +#[test] +fn rewards_delegator_replaced() { + ExtBuilder::default() + .with_balances(vec![ + (1, 2 * DECIMALS), + (2, 2 * DECIMALS), + (3, 2 * DECIMALS), + (4, 2 * DECIMALS), + (5, 2 * DECIMALS), + (6, 2 * DECIMALS), + ]) + .with_collators(vec![(1, 2 * DECIMALS)]) + .with_delegators(vec![ + (2, 1, 2 * DECIMALS), + (3, 1, 2 * DECIMALS), + (4, 1, 2 * DECIMALS), + (5, 1, DECIMALS), + ]) + .build() + .execute_with(|| { + // note once to set counter to 1 + StakePallet::note_author(1); + assert_eq!(StakePallet::reward_count(1), 1); + + // 6 kicks 5 + assert_ok!(StakePallet::join_delegators(Origin::signed(6), 1, 2 * DECIMALS)); + // 5 should have rewards and counter updated + assert!(!StakePallet::rewards(5).is_zero()); + assert_eq!(StakePallet::reward_count(5), 1); + // 6 should not have rewards but same counter as former collator + assert!(StakePallet::rewards(6).is_zero()); + assert_eq!(StakePallet::reward_count(6), 1); + }); +} + +// FIXME: Re-enable +// #[test] +// fn rewards_delegator_revokes_single_delegation() { +// ExtBuilder::default() +// .with_balances(vec![(1, DECIMALS), (2, 2 * DECIMALS)]) +// .with_collators(vec![(1, DECIMALS)]) +// .with_delegators(vec![(2, 1, DECIMALS)]) +// .build() +// .execute_with(|| { +// // note once to set counter to 1 +// StakePallet::note_author(1); +// assert_eq!(StakePallet::reward_count(1), 1); +// assert!(StakePallet::reward_count(2).is_zero()); +// assert!(StakePallet::reward_count(3).is_zero()); +// (1..=3).for_each(|id| { +// assert!(StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); +// }); + +// // stake more to trigger reward incrementing just for 3 +// assert_ok!(StakePallet::delegator_stake_more(Origin::signed(2), 1, +// DECIMALS)); // 1 should still have counter 1 but no rewards +// assert_eq!(StakePallet::reward_count(1), 1); +// assert!(StakePallet::rewards(1).is_zero()); +// // 2 should still have neither rewards nor counter +// assert!(StakePallet::reward_count(2).is_zero()); +// assert!(StakePallet::rewards(2).is_zero()); +// // 3 should have rewards and the same counter as 1 +// assert_eq!(StakePallet::reward_count(3), 1); +// assert!(!StakePallet::rewards(3).is_zero()); +// }); +// } + +// #[test] +// fn rewards_delegator_leaves() { +// ExtBuilder::default() +// .with_balances(vec![(1, DECIMALS), (2, DECIMALS), (3, 2 * DECIMALS)]) +// .with_collators(vec![(1, DECIMALS), (2, DECIMALS)]) +// .with_delegators(vec![(3, 1, DECIMALS)]) +// .build() +// .execute_with(|| { +// assert_ok!(StakePallet::delegate_another_candidate(Origin::signed(3), 2, +// DECIMALS)); // note both collators once to set their counter to 2 +// StakePallet::note_author(1); +// StakePallet::note_author(2); +// assert_eq!(StakePallet::reward_count(1), 2); +// assert_eq!(StakePallet::reward_count(2), 2); +// assert!(StakePallet::reward_count(3).is_zero()); +// (1..=3).for_each(|id| { +// assert!(StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); +// }); + +// // only 3 should have non-zero rewards and counter reset +// assert_ok!(StakePallet::leave_delegators(Origin::signed(3))); +// (1..=2).for_each(|id| { +// assert_eq!(StakePallet::reward_count(id), 2, "acc_id {:?}", id); +// assert!(StakePallet::rewards(id).is_zero(), "acc_id {:?}", id); +// }); +// assert!(!StakePallet::rewards(3).is_zero()); +// assert_eq!(StakePallet::reward_count(3), 4); +// }); +// } + +// TODO: +// rewards_delegator_revokes_single_delegation +// rewards_delegator_revokes_all_delegations +// rewards_delegator_delegates_another_candidate +// rewards_delegator_delegates_another_candidate_replacing +// rewards_set_inflation +// rewards_annual_inflation_adjustment diff --git a/pallets/parachain-staking/src/types.rs b/pallets/parachain-staking/src/types.rs index 64bb6b7392..6c8ccaa117 100644 --- a/pallets/parachain-staking/src/types.rs +++ b/pallets/parachain-staking/src/types.rs @@ -26,10 +26,8 @@ use sp_runtime::{ use sp_staking::SessionIndex; use sp_std::{ cmp::Ordering, - convert::TryInto, fmt::Debug, ops::{Add, Sub}, - vec, }; use crate::{set::OrderedSet, Config}; @@ -211,100 +209,60 @@ where } } -#[derive(Encode, Decode, RuntimeDebug, PartialEq, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(MaxCollatorsPerDelegator))] -#[codec(mel_bound(AccountId: MaxEncodedLen, Balance: MaxEncodedLen))] -pub struct Delegator> { - pub delegations: OrderedSet, MaxCollatorsPerDelegator>, - pub total: Balance, -} - -impl Delegator +pub type Delegator = Stake, Balance>; +impl Delegator where AccountId: Eq + Ord + Clone + Debug, - Balance: Copy + Add + Saturating + PartialOrd + Eq + Ord + Debug + Zero, - MaxCollatorsPerDelegator: Get + Debug + PartialEq, + Balance: Copy + Add + Saturating + PartialOrd + Eq + Ord + Debug + Zero + Default, { - pub fn try_new(collator: AccountId, amount: Balance) -> Result { - Ok(Delegator { - delegations: OrderedSet::from( - vec![Stake { - owner: collator, - amount, - }] - .try_into()?, - ), - total: amount, - }) - } - /// Adds a new delegation. /// - /// If already delegating to the same account, this call returns false and - /// doesn't insert the new delegation. - pub fn add_delegation(&mut self, stake: Stake) -> Result { - let amt = stake.amount; - if self.delegations.try_insert(stake).map_err(|_| ())? { - self.total = self.total.saturating_add(amt); - Ok(true) + /// If already delegating to someone, this call will fail. + pub fn add_delegation(&mut self, stake: Stake) -> Result<(), ()> { + if self.owner.is_none() && self.amount.is_zero() { + self.owner = Some(stake.owner); + self.amount = stake.amount; + Ok(()) } else { - Ok(false) + Err(()) } } - /// Returns Some(remaining stake for delegator) if the delegation for the - /// collator exists. Returns `None` otherwise. - pub fn rm_delegation(&mut self, collator: &AccountId) -> Option { - let amt = self.delegations.remove(&Stake:: { - owner: collator.clone(), - // amount is irrelevant for removal - amount: Balance::zero(), - }); - - if let Some(Stake:: { amount: balance, .. }) = amt { - self.total = self.total.saturating_sub(balance); - Some(self.total) + /// Returns Ok if the delegation for the + /// collator exists and `Err` otherwise. + pub fn rm_delegation(&mut self, collator: AccountId) -> Result<(), ()> { + if self.owner == Some(collator) { + self.amount = Balance::zero(); + self.owner = None; + Ok(()) } else { - None + Err(()) } } - /// Returns Some(delegated_amount) if successfull, None if delegation was + /// Returns Ok(delegated_amount) if successfull, `Err` if delegation was /// not found. - pub fn inc_delegation(&mut self, collator: AccountId, more: Balance) -> Option { - if let Ok(i) = self.delegations.linear_search(&Stake:: { - owner: collator, - amount: Balance::zero(), - }) { - self.delegations - .mutate(|vec| vec[i].amount = vec[i].amount.saturating_add(more)); - self.total = self.total.saturating_add(more); - self.delegations.sort_greatest_to_lowest(); - Some(self.delegations[i].amount) + pub fn inc_delegation(&mut self, collator: AccountId, more: Balance) -> Result { + if self.owner == Some(collator) { + self.amount = self.amount.saturating_add(more); + Ok(self.amount) } else { - None + Err(()) } } - /// Returns Some(Some(delegated_amount)) if successful, None if delegation - /// was not found and Some(None) if delegated stake would underflow. - pub fn dec_delegation(&mut self, collator: AccountId, less: Balance) -> Option> { - if let Ok(i) = self.delegations.linear_search(&Stake:: { - owner: collator, - amount: Balance::zero(), - }) { - if self.delegations[i].amount > less { - self.delegations - .mutate(|vec| vec[i].amount = vec[i].amount.saturating_sub(less)); - self.total = self.total.saturating_sub(less); - self.delegations.sort_greatest_to_lowest(); - Some(Some(self.delegations[i].amount)) + /// Returns Ok(Some(delegated_amount)) if successful, `Err` if delegation + /// was not found and Ok(None) if delegated stake would underflow. + pub fn dec_delegation(&mut self, collator: AccountId, less: Balance) -> Result, ()> { + if self.owner == Some(collator) { + if self.amount > less { + self.amount = self.amount.saturating_sub(less); + Ok(Some(self.amount)) } else { - // underflow error; should rm entire delegation - Some(None) + Ok(None) } } else { - None + Err(()) } } } @@ -372,13 +330,6 @@ pub struct DelegationCounter { pub counter: u32, } -/// Internal type which is only used when a delegator is replaced by another -/// one to delay the storage entry removal until failure cannot happen anymore. -pub(crate) struct ReplacedDelegator { - pub who: AccountIdOf, - pub state: Option, BalanceOf, T::MaxCollatorsPerDelegator>>, -} - pub type AccountIdOf = ::AccountId; pub type BalanceOf = <::Currency as Currency>>::Balance; pub type CandidateOf = Candidate, BalanceOf, S>; diff --git a/runtimes/common/src/constants.rs b/runtimes/common/src/constants.rs index 06b4b38415..e0033a45ff 100644 --- a/runtimes/common/src/constants.rs +++ b/runtimes/common/src/constants.rs @@ -203,9 +203,6 @@ pub mod staking { /// Maximum 25 delegators per collator at launch, might be increased later #[derive(Debug, PartialEq)] pub const MaxDelegatorsPerCollator: u32 = MAX_DELEGATORS_PER_COLLATOR; - /// Maximum 1 collator per delegator at launch, will be increased later - #[derive(Debug, PartialEq)] - pub const MaxCollatorsPerDelegator: u32 = 1; /// Minimum stake required to be reserved to be a collator is 10_000 pub const MinCollatorStake: Balance = 10_000 * KILT; /// Minimum stake required to be reserved to be a delegator is 1000 diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index c93fd8e367..44ca67e127 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -624,11 +624,9 @@ impl parachain_staking::Config for Runtime { type MinRequiredCollators = constants::staking::MinRequiredCollators; type MaxDelegationsPerRound = constants::staking::MaxDelegationsPerRound; type MaxDelegatorsPerCollator = constants::staking::MaxDelegatorsPerCollator; - type MaxCollatorsPerDelegator = constants::staking::MaxCollatorsPerDelegator; type MinCollatorStake = constants::staking::MinCollatorStake; type MinCollatorCandidateStake = constants::staking::MinCollatorStake; type MaxTopCandidates = constants::staking::MaxCollatorCandidates; - type MinDelegation = constants::staking::MinDelegatorStake; type MinDelegatorStake = constants::staking::MinDelegatorStake; type MaxUnstakeRequests = constants::staking::MaxUnstakeRequests; type NetworkRewardRate = constants::staking::NetworkRewardRate; diff --git a/runtimes/spiritnet/src/lib.rs b/runtimes/spiritnet/src/lib.rs index a62ebaf100..5e81a21974 100644 --- a/runtimes/spiritnet/src/lib.rs +++ b/runtimes/spiritnet/src/lib.rs @@ -619,11 +619,9 @@ impl parachain_staking::Config for Runtime { type MinRequiredCollators = constants::staking::MinRequiredCollators; type MaxDelegationsPerRound = constants::staking::MaxDelegationsPerRound; type MaxDelegatorsPerCollator = constants::staking::MaxDelegatorsPerCollator; - type MaxCollatorsPerDelegator = constants::staking::MaxCollatorsPerDelegator; type MinCollatorStake = constants::staking::MinCollatorStake; type MinCollatorCandidateStake = constants::staking::MinCollatorStake; type MaxTopCandidates = constants::staking::MaxCollatorCandidates; - type MinDelegation = constants::staking::MinDelegatorStake; type MinDelegatorStake = constants::staking::MinDelegatorStake; type MaxUnstakeRequests = constants::staking::MaxUnstakeRequests; type NetworkRewardRate = constants::staking::NetworkRewardRate;