From f27853b2d8f432737d533119cd60fae46872d78b Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 19 Nov 2024 16:52:47 +0100 Subject: [PATCH] WIP update to 0.39.0 --- Cargo.lock | 21 + pallets/parachain-staking-old/Cargo.toml | 44 + pallets/parachain-staking-old/README.md | 28 + pallets/parachain-staking-old/migrations.md | 1 + .../src/auto_compound.rs | 353 + .../parachain-staking-old/src/benchmarks.rs | 2486 ++++++ .../src/delegation_requests.rs | 532 ++ .../parachain-staking-old/src/inflation.rs | 250 + pallets/parachain-staking-old/src/lib.rs | 2207 ++++++ .../parachain-staking-old/src/migrations.rs | 17 + pallets/parachain-staking-old/src/mock.rs | 785 ++ .../src/rewards/mint_rewards.rs | 113 + .../parachain-staking-old/src/rewards/mod.rs | 153 + .../rewards/transfer_from_rewards_account.rs | 227 + pallets/parachain-staking-old/src/set.rs | 166 + pallets/parachain-staking-old/src/tests.rs | 6835 +++++++++++++++++ pallets/parachain-staking-old/src/traits.rs | 83 + pallets/parachain-staking-old/src/types.rs | 1642 ++++ pallets/parachain-staking-old/src/weights.rs | 1853 +++++ pallets/parachain-staking/Cargo.toml | 3 +- pallets/parachain-staking/README.md | 2 - pallets/parachain-staking/migrations.md | 41 + .../parachain-staking/src/auto_compound.rs | 149 +- pallets/parachain-staking/src/benchmarks.rs | 171 +- .../src/delegation_requests.rs | 121 +- pallets/parachain-staking/src/inflation.rs | 102 +- pallets/parachain-staking/src/lib.rs | 444 +- pallets/parachain-staking/src/migrations.rs | 198 +- pallets/parachain-staking/src/mock.rs | 483 +- pallets/parachain-staking/src/set.rs | 18 +- pallets/parachain-staking/src/tests.rs | 3823 ++++++--- pallets/parachain-staking/src/traits.rs | 14 +- pallets/parachain-staking/src/types.rs | 300 +- pallets/parachain-staking/src/weights.rs | 2480 +++--- precompiles/parachain-staking/src/mock.rs | 1 - runtime/laos/src/configs/parachain_staking.rs | 16 +- 36 files changed, 23311 insertions(+), 2851 deletions(-) create mode 100644 pallets/parachain-staking-old/Cargo.toml create mode 100644 pallets/parachain-staking-old/README.md create mode 100644 pallets/parachain-staking-old/migrations.md create mode 100644 pallets/parachain-staking-old/src/auto_compound.rs create mode 100644 pallets/parachain-staking-old/src/benchmarks.rs create mode 100644 pallets/parachain-staking-old/src/delegation_requests.rs create mode 100644 pallets/parachain-staking-old/src/inflation.rs create mode 100644 pallets/parachain-staking-old/src/lib.rs create mode 100644 pallets/parachain-staking-old/src/migrations.rs create mode 100644 pallets/parachain-staking-old/src/mock.rs create mode 100644 pallets/parachain-staking-old/src/rewards/mint_rewards.rs create mode 100644 pallets/parachain-staking-old/src/rewards/mod.rs create mode 100644 pallets/parachain-staking-old/src/rewards/transfer_from_rewards_account.rs create mode 100644 pallets/parachain-staking-old/src/set.rs create mode 100644 pallets/parachain-staking-old/src/tests.rs create mode 100644 pallets/parachain-staking-old/src/traits.rs create mode 100644 pallets/parachain-staking-old/src/types.rs create mode 100644 pallets/parachain-staking-old/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 55cb83b22..aa448e61e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7949,6 +7949,27 @@ dependencies = [ [[package]] name = "pallet-parachain-staking" version = "3.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "similar-asserts", + "sp-consensus-slots", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.11.0)", + "substrate-fixed", +] + +[[package]] +name = "pallet-parachain-staking-old" +version = "3.0.0" dependencies = [ "frame-benchmarking", "frame-support", diff --git a/pallets/parachain-staking-old/Cargo.toml b/pallets/parachain-staking-old/Cargo.toml new file mode 100644 index 000000000..b30b6965e --- /dev/null +++ b/pallets/parachain-staking-old/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "pallet-parachain-staking-old" +authors = { workspace = true } +description = "parachain staking pallet for collator selection and reward distribution" +edition = "2021" +version = "3.0.0" + +[dependencies] +log = { workspace = true } +serde = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive" ] } +scale-info = { workspace = true, features = [ "derive" ] } +sp-consensus-slots = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +substrate-fixed = { workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true, features = [ "insecure_zero_ed", "std" ] } +similar-asserts = { workspace = true } +sp-core = { workspace = true, features = [ "std" ] } +sp-io = { workspace = true, features = [ "std" ] } +sp-runtime = { workspace = true } +test-utils = { workspace = true } + +[features] +default = [ "std" ] +std = [ + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-consensus-slots/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ "frame-benchmarking" ] +try-runtime = [ "frame-support/try-runtime" ] diff --git a/pallets/parachain-staking-old/README.md b/pallets/parachain-staking-old/README.md new file mode 100644 index 000000000..499c9de3d --- /dev/null +++ b/pallets/parachain-staking-old/README.md @@ -0,0 +1,28 @@ +# DPoS Pallet for Parachain Staking + +This pallet is forked from [`moonbeam v0.36.0`](https://github.com/moonbeam-foundation/moonbeam/tree/v0.36.0/pallets/parachain-staking), commit hash `d1087f3091726ffbe14b44655d848d00a1f14201`. + +## Formatting Rules + +- dependencies in alphabetical order in the `Cargo.toml` and at the top of each file +- prefer explicit imports to glob import syntax i.e. prefer `use::crate::{Ex1, Ex2, ..};` to `use super::*;` + +## Description + +Implements Delegated Proof of Stake to + +1. select the active set of eligible block producers +2. reward block authors +3. enable delegators and collators to participate in inflationary rewards + +Links: + +- [Rust Documentation](https://moonbeam-foundation.github.io/moonbeam/pallet_parachain_staking/index.html) +- [Unofficial Documentation](https://meta5.world/parachain-staking-docs/) +- [(Outdated) Blog Post with Justification](https://meta5.world/posts/parachain-staking) + +## History + +Since January 2021, Moonbeam's team has maintained this Delegated Proof of Stake (DPoS) pallet designed specifically for parachains. + +Since April 2021, the development of this pallet has been supported by [a Web3 Foundation grant](https://github.com/w3f/Grants-Program/pull/389). The [first milestone](https://github.com/w3f/Grant-Milestone-Delivery/pull/218) was approved in June 2021. diff --git a/pallets/parachain-staking-old/migrations.md b/pallets/parachain-staking-old/migrations.md new file mode 100644 index 000000000..220203115 --- /dev/null +++ b/pallets/parachain-staking-old/migrations.md @@ -0,0 +1 @@ +# Migration History diff --git a/pallets/parachain-staking-old/src/auto_compound.rs b/pallets/parachain-staking-old/src/auto_compound.rs new file mode 100644 index 000000000..793e70448 --- /dev/null +++ b/pallets/parachain-staking-old/src/auto_compound.rs @@ -0,0 +1,353 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! Auto-compounding functionality for staking rewards + +use crate::{ + pallet::{ + AddGet, AutoCompoundingDelegations as AutoCompoundingDelegationsStorage, BalanceOf, + CandidateInfo, Config, DelegatorState, Error, Event, Pallet, Total, + }, + types::{Bond, BondAdjust, Delegator}, +}; +use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, traits::Get}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::{traits::Saturating, BoundedVec, Percent, RuntimeDebug}; +use sp_std::prelude::*; + +/// Represents the auto-compounding amount for a delegation. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, PartialOrd, Ord)] +pub struct AutoCompoundConfig { + pub delegator: AccountId, + pub value: Percent, +} + +pub type AddGetOf = AddGet< + ::MaxTopDelegationsPerCandidate, + ::MaxBottomDelegationsPerCandidate, +>; +/// Represents the auto-compounding [Delegations] for `T: Config` +#[derive(Clone, Eq, PartialEq, RuntimeDebug)] +pub struct AutoCompoundDelegations( + BoundedVec, AddGetOf>, +); + +impl AutoCompoundDelegations +where + T: Config, +{ + /// Creates a new instance of [AutoCompoundingDelegations] from a vector of sorted_delegations. + /// This is used for testing purposes only. + #[cfg(test)] + pub fn new( + sorted_delegations: BoundedVec, AddGetOf>, + ) -> Self { + Self(sorted_delegations) + } + + pub fn get_auto_compounding_delegation_count(candidate: &T::AccountId) -> usize { + >::decode_len(candidate).unwrap_or_default() + } + + /// Retrieves an instance of [AutoCompoundingDelegations] storage as [AutoCompoundDelegations]. + pub fn get_storage(candidate: &T::AccountId) -> Self { + Self(>::get(candidate)) + } + + /// Inserts the current state to [AutoCompoundingDelegations] storage. + pub fn set_storage(self, candidate: &T::AccountId) { + >::insert(candidate, self.0) + } + + /// Retrieves the auto-compounding value for a delegation. The `delegations_config` must be a + /// sorted vector for binary_search to work. + pub fn get_for_delegator(&self, delegator: &T::AccountId) -> Option { + match self.0.binary_search_by(|d| d.delegator.cmp(delegator)) { + Ok(index) => Some(self.0[index].value), + Err(_) => None, + } + } + + /// Sets the auto-compounding value for a delegation. The `delegations_config` must be a sorted + /// vector for binary_search to work. + pub fn set_for_delegator( + &mut self, + delegator: T::AccountId, + value: Percent, + ) -> Result> { + match self.0.binary_search_by(|d| d.delegator.cmp(&delegator)) { + Ok(index) => + if self.0[index].value == value { + Ok(false) + } else { + self.0[index].value = value; + Ok(true) + }, + Err(index) => { + self.0 + .try_insert(index, AutoCompoundConfig { delegator, value }) + .map_err(|_| Error::::ExceedMaxDelegationsPerDelegator)?; + Ok(true) + }, + } + } + + /// Removes the auto-compounding value for a delegation. + /// Returns `true` if the entry was removed, `false` otherwise. The `delegations_config` must be + /// a sorted vector for binary_search to work. + pub fn remove_for_delegator(&mut self, delegator: &T::AccountId) -> bool { + match self.0.binary_search_by(|d| d.delegator.cmp(delegator)) { + Ok(index) => { + self.0.remove(index); + true + }, + Err(_) => false, + } + } + + /// Returns the length of the inner vector. + pub fn len(&self) -> u32 { + self.0.len() as u32 + } + + /// Is the inner vector empty? + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns a reference to the inner vector. + #[cfg(test)] + pub fn inner(&self) -> &BoundedVec, AddGetOf> { + &self.0 + } + + /// Converts the [AutoCompoundDelegations] into the inner vector. + #[cfg(test)] + pub fn into_inner(self) -> BoundedVec, AddGetOf> { + self.0 + } + + // -- pallet functions -- + + /// Delegates and sets the auto-compounding config. The function skips inserting auto-compound + /// storage and validation, if the auto-compound value is 0%. + pub(crate) fn delegate_with_auto_compound( + candidate: T::AccountId, + delegator: T::AccountId, + amount: BalanceOf, + auto_compound: Percent, + candidate_delegation_count_hint: u32, + candidate_auto_compounding_delegation_count_hint: u32, + delegation_count_hint: u32, + ) -> DispatchResultWithPostInfo { + // check that caller can lock the amount before any changes to storage + ensure!( + >::get_delegator_stakable_free_balance(&delegator) >= amount, + Error::::InsufficientBalance + ); + ensure!(amount >= T::MinDelegation::get(), Error::::DelegationBelowMin); + + let mut delegator_state = if let Some(mut state) = >::get(&delegator) { + // delegation after first + ensure!( + delegation_count_hint >= state.delegations.0.len() as u32, + Error::::TooLowDelegationCountToDelegate + ); + ensure!( + (state.delegations.0.len() as u32) < T::MaxDelegationsPerDelegator::get(), + Error::::ExceedMaxDelegationsPerDelegator + ); + ensure!( + state.add_delegation(Bond { owner: candidate.clone(), amount }), + Error::::AlreadyDelegatedCandidate + ); + state + } else { + // first delegation + ensure!(!>::is_candidate(&delegator), Error::::CandidateExists); + Delegator::new(delegator.clone(), candidate.clone(), amount) + }; + let mut candidate_state = + >::get(&candidate).ok_or(Error::::CandidateDNE)?; + ensure!( + candidate_delegation_count_hint >= candidate_state.delegation_count, + Error::::TooLowCandidateDelegationCountToDelegate + ); + + if !auto_compound.is_zero() { + ensure!( + Self::get_auto_compounding_delegation_count(&candidate) as u32 <= + candidate_auto_compounding_delegation_count_hint, + >::TooLowCandidateAutoCompoundingDelegationCountToDelegate, + ); + } + + // add delegation to candidate + let (delegator_position, less_total_staked) = candidate_state + .add_delegation::(&candidate, Bond { owner: delegator.clone(), amount })?; + + // lock delegator amount + delegator_state.adjust_bond_lock::(BondAdjust::Increase(amount))?; + + // adjust total locked, + // only is_some if kicked the lowest bottom as a consequence of this new delegation + let net_total_increase = + if let Some(less) = less_total_staked { amount.saturating_sub(less) } else { amount }; + let new_total_locked = >::get().saturating_add(net_total_increase); + + // set auto-compound config if the percent is non-zero + if !auto_compound.is_zero() { + let mut auto_compounding_state = Self::get_storage(&candidate); + auto_compounding_state.set_for_delegator(delegator.clone(), auto_compound)?; + auto_compounding_state.set_storage(&candidate); + } + + >::put(new_total_locked); + >::insert(&candidate, candidate_state); + >::insert(&delegator, delegator_state); + >::deposit_event(Event::Delegation { + delegator, + locked_amount: amount, + candidate, + delegator_position, + auto_compound, + }); + + Ok(().into()) + } + + /// Sets the auto-compounding value for a delegation. The config is removed if value is zero. + pub(crate) fn set_auto_compound( + candidate: T::AccountId, + delegator: T::AccountId, + value: Percent, + candidate_auto_compounding_delegation_count_hint: u32, + delegation_count_hint: u32, + ) -> DispatchResultWithPostInfo { + let delegator_state = + >::get(&delegator).ok_or(>::DelegatorDNE)?; + ensure!( + delegator_state.delegations.0.len() <= delegation_count_hint as usize, + >::TooLowDelegationCountToAutoCompound, + ); + ensure!( + delegator_state.delegations.0.iter().any(|b| b.owner == candidate), + >::DelegationDNE, + ); + + let mut auto_compounding_state = Self::get_storage(&candidate); + ensure!( + auto_compounding_state.len() <= candidate_auto_compounding_delegation_count_hint, + >::TooLowCandidateAutoCompoundingDelegationCountToAutoCompound, + ); + let state_updated = if value.is_zero() { + auto_compounding_state.remove_for_delegator(&delegator) + } else { + auto_compounding_state.set_for_delegator(delegator.clone(), value)? + }; + if state_updated { + auto_compounding_state.set_storage(&candidate); + } + + >::deposit_event(Event::AutoCompoundSet { candidate, delegator, value }); + + Ok(().into()) + } + + /// Removes the auto-compounding value for a delegation. This should be called when the + /// delegation is revoked to cleanup storage. Storage is only written iff the entry existed. + pub(crate) fn remove_auto_compound(candidate: &T::AccountId, delegator: &T::AccountId) { + let mut auto_compounding_state = Self::get_storage(candidate); + if auto_compounding_state.remove_for_delegator(delegator) { + auto_compounding_state.set_storage(candidate); + } + } + + /// Returns the value of auto-compound, if it exists for a given delegation, zero otherwise. + pub(crate) fn auto_compound(candidate: &T::AccountId, delegator: &T::AccountId) -> Percent { + let delegations_config = Self::get_storage(candidate); + delegations_config.get_for_delegator(delegator).unwrap_or_else(Percent::zero) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::Test; + + #[test] + fn test_set_for_delegator_inserts_config_and_returns_true_if_entry_missing() { + let mut delegations_config = + AutoCompoundDelegations::::new(vec![].try_into().expect("must succeed")); + assert!(delegations_config + .set_for_delegator(1, Percent::from_percent(50)) + .expect("must succeed")); + assert_eq!( + vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(50) }], + delegations_config.into_inner().into_inner(), + ); + } + + #[test] + fn test_set_for_delegator_updates_config_and_returns_true_if_entry_changed() { + let mut delegations_config = AutoCompoundDelegations::::new( + vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }] + .try_into() + .expect("must succeed"), + ); + assert!(delegations_config + .set_for_delegator(1, Percent::from_percent(50)) + .expect("must succeed")); + assert_eq!( + vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(50) }], + delegations_config.into_inner().into_inner(), + ); + } + + #[test] + fn test_set_for_delegator_updates_config_and_returns_false_if_entry_unchanged() { + let mut delegations_config = AutoCompoundDelegations::::new( + vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }] + .try_into() + .expect("must succeed"), + ); + assert!(!delegations_config + .set_for_delegator(1, Percent::from_percent(10)) + .expect("must succeed")); + assert_eq!( + vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }], + delegations_config.into_inner().into_inner(), + ); + } + + #[test] + fn test_remove_for_delegator_returns_false_if_entry_was_missing() { + let mut delegations_config = + AutoCompoundDelegations::::new(vec![].try_into().expect("must succeed")); + assert!(!delegations_config.remove_for_delegator(&1),); + } + + #[test] + fn test_remove_delegation_config_returns_true_if_entry_existed() { + let mut delegations_config = AutoCompoundDelegations::::new( + vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }] + .try_into() + .expect("must succeed"), + ); + assert!(delegations_config.remove_for_delegator(&1)); + } +} diff --git a/pallets/parachain-staking-old/src/benchmarks.rs b/pallets/parachain-staking-old/src/benchmarks.rs new file mode 100644 index 000000000..969574f93 --- /dev/null +++ b/pallets/parachain-staking-old/src/benchmarks.rs @@ -0,0 +1,2486 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +#![cfg(feature = "runtime-benchmarks")] + +//! Benchmarking +use crate::{ + AwardedPts, BalanceOf, BottomDelegations, Call, CandidateBondLessRequest, Config, + DelegationAction, EnableMarkingOffline, Pallet, ParachainBondConfig, ParachainBondInfo, Points, + Range, RewardPayment, Round, ScheduledRequest, Staked, TopDelegations, +}; +use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; +use frame_support::traits::{Currency, Get, OnFinalize, OnInitialize}; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; +use sp_runtime::{traits::Zero, Perbill, Percent}; +use sp_std::vec::Vec; + +/// Minimum collator candidate stake +fn min_candidate_stk() -> BalanceOf { + <::MinCandidateStk as Get>>::get() +} + +/// Minimum delegator stake +fn min_delegator_stk() -> BalanceOf { + <::MinDelegation as Get>>::get() +} + +/// Create a funded user. +/// Extra + min_candidate_stk is total minted funds +/// Returns tuple (id, balance) +fn create_funded_user( + string: &'static str, + n: u32, + extra: BalanceOf, +) -> (T::AccountId, BalanceOf) { + const SEED: u32 = 0; + let user = account(string, n, SEED); + let min_candidate_stk = min_candidate_stk::(); + let total = min_candidate_stk + extra; + T::Currency::make_free_balance_be(&user, total); + let _ = T::Currency::issue(total); + (user, total) +} + +/// Create a funded delegator. +fn create_funded_delegator( + string: &'static str, + n: u32, + extra: BalanceOf, + collator: T::AccountId, + min_bond: bool, + collator_delegator_count: u32, +) -> Result { + let (user, total) = create_funded_user::(string, n, extra); + let bond = if min_bond { min_delegator_stk::() } else { total }; + Pallet::::delegate( + RawOrigin::Signed(user.clone()).into(), + collator, + bond, + collator_delegator_count, + 0u32, // first delegation for all calls + )?; + Ok(user) +} + +#[allow(dead_code)] +enum Amount { + All, + Value(T), +} + +#[allow(dead_code)] +enum AccountBalance { + MinDelegatorStake, + MinCandidateStake, + Value(T), +} + +#[allow(dead_code)] +enum AccountAction { + None, + Delegate { + collator: T::AccountId, + amount: Amount>, + auto_compound: Percent, + collator_delegation_count: u32, + collator_auto_compound_delegation_count: u32, + }, + JoinCandidates { + amount: Amount>, + candidate_count: u32, + }, +} + +/// Create an account. +fn create_account( + seed_name: &'static str, + seed_index: u32, + balance: AccountBalance>, + action: AccountAction, +) -> Result { + let acc = account(seed_name, seed_index, 0u32); + let initial_balance = match balance { + AccountBalance::MinCandidateStake => min_candidate_stk::(), + AccountBalance::MinDelegatorStake => min_delegator_stk::(), + AccountBalance::Value(v) => v, + }; + + let _ = T::Currency::make_free_balance_be(&acc, initial_balance); + let _ = T::Currency::issue(initial_balance); + + match action { + AccountAction::None => (), + AccountAction::Delegate { + collator, + amount, + auto_compound, + collator_delegation_count, + collator_auto_compound_delegation_count, + } => { + let amount = match amount { + Amount::All => initial_balance, + Amount::Value(v) => v, + }; + Pallet::::delegate_with_auto_compound( + RawOrigin::Signed(acc.clone()).into(), + collator, + amount, + auto_compound, + collator_delegation_count, + collator_auto_compound_delegation_count, + 0u32, // first delegation for all calls + ) + .expect("failed delegating"); + }, + AccountAction::JoinCandidates { amount, candidate_count } => { + let amount = match amount { + Amount::All => initial_balance, + Amount::Value(v) => v, + }; + Pallet::::join_candidates( + RawOrigin::Signed(acc.clone()).into(), + amount, + candidate_count, + ) + .expect("failed joining candidates"); + }, + }; + + Ok(acc) +} + +/// Create a funded collator. +fn create_funded_collator( + string: &'static str, + n: u32, + extra: BalanceOf, + min_bond: bool, + candidate_count: u32, +) -> Result { + let (user, total) = create_funded_user::(string, n, extra); + let bond = if min_bond { min_candidate_stk::() } else { total }; + Pallet::::join_candidates(RawOrigin::Signed(user.clone()).into(), bond, candidate_count)?; + Ok(user) +} + +// Simulate staking on finalize by manually setting points +fn parachain_staking_on_finalize(author: T::AccountId) { + let now = >::get().current; + let score_plus_20 = >::get(now, &author).saturating_add(20); + >::insert(now, author, score_plus_20); + >::mutate(now, |x| *x = x.saturating_add(20)); +} + +/// Run to end block and author +fn roll_to_and_author(round_delay: u32, author: T::AccountId) { + let total_rounds = round_delay + 1u32; + let round_length: BlockNumberFor = Pallet::::round().length.into(); + let mut now = >::block_number() + 1u32.into(); + let first: BlockNumberFor = (Pallet::::round().first as u32).into(); + let end = first + (round_length * total_rounds.into()); + while now < end { + parachain_staking_on_finalize::(author.clone()); + >::on_finalize(>::block_number()); + >::set_block_number( + >::block_number() + 1u32.into(), + ); + >::on_initialize(>::block_number()); + Pallet::::on_initialize(>::block_number()); + now += 1u32.into(); + } +} + +const USER_SEED: u32 = 999666; +struct Seed { + pub inner: u32, +} +impl Seed { + fn new() -> Self { + Seed { inner: USER_SEED } + } + + pub fn take(&mut self) -> u32 { + let v = self.inner; + self.inner += 1; + v + } +} + +struct DecreasingBalance { + pub initial: BalanceOf, + pub dec_by: BalanceOf, +} +impl DecreasingBalance { + fn new(initial: BalanceOf, dec_by: BalanceOf) -> Self { + DecreasingBalance { initial, dec_by } + } + + pub fn take(&mut self) -> BalanceOf { + let v = self.initial; + self.initial -= self.dec_by; + v + } +} + +benchmarks! { + // MONETARY ORIGIN DISPATCHABLES + set_staking_expectations { + let stake_range: Range> = Range { + min: 100u32.into(), + ideal: 200u32.into(), + max: 300u32.into(), + }; + }: _(RawOrigin::Root, stake_range) + verify { + assert_eq!(Pallet::::inflation_config().expect, stake_range); + } + + set_inflation { + let inflation_range: Range = Range { + min: Perbill::from_perthousand(1), + ideal: Perbill::from_perthousand(2), + max: Perbill::from_perthousand(3), + }; + + }: _(RawOrigin::Root, inflation_range) + verify { + assert_eq!(Pallet::::inflation_config().annual, inflation_range); + } + + set_parachain_bond_account { + let parachain_bond_account: T::AccountId = account("TEST", 0u32, USER_SEED); + }: _(RawOrigin::Root, parachain_bond_account.clone()) + verify { + assert_eq!(Pallet::::parachain_bond_info().account, parachain_bond_account); + } + + set_parachain_bond_reserve_percent { + }: _(RawOrigin::Root, Percent::from_percent(33)) + verify { + assert_eq!(Pallet::::parachain_bond_info().percent, Percent::from_percent(33)); + } + + // ROOT DISPATCHABLES + + set_total_selected { + Pallet::::set_blocks_per_round(RawOrigin::Root.into(), 101u32)?; + }: _(RawOrigin::Root, 100u32) + verify { + assert_eq!(Pallet::::total_selected(), 100u32); + } + + set_collator_commission {}: _(RawOrigin::Root, Perbill::from_percent(33)) + verify { + assert_eq!(Pallet::::collator_commission(), Perbill::from_percent(33)); + } + + set_blocks_per_round {}: _(RawOrigin::Root, 1200u32) + verify { + assert_eq!(Pallet::::round().length, 1200u32); + } + + // USER DISPATCHABLES + + join_candidates { + let x in 3..T::MaxCandidates::get(); + // Worst Case Complexity is insertion into an ordered list so \exists full list before call + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + 0u32.into(), + true, + candidate_count + )?; + candidate_count += 1u32; + } + let (caller, min_candidate_stk) = create_funded_user::("caller", USER_SEED, 0u32.into()); + }: _(RawOrigin::Signed(caller.clone()), min_candidate_stk, candidate_count) + verify { + assert!(Pallet::::is_candidate(&caller)); + } + + // This call schedules the collator's exit and removes them from the candidate pool + // -> it retains the self-bond and delegator bonds + schedule_leave_candidates { + let x in 3..T::MaxCandidates::get(); + // Worst Case Complexity is removal from an ordered list so \exists full list before call + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + 0u32.into(), + true, + candidate_count + )?; + candidate_count += 1u32; + } + let caller: T::AccountId = create_funded_collator::( + "caller", + USER_SEED, + 0u32.into(), + true, + candidate_count, + )?; + candidate_count += 1u32; + }: _(RawOrigin::Signed(caller.clone()), candidate_count) + verify { + assert!(Pallet::::candidate_info(&caller).expect("must exist").is_leaving()); + } + + execute_leave_candidates_worst_case { + // x is total number of delegations for the candidate + // Note: For our base scenario, we assume all delegations are auto-compounding + let x in 2..( + <::MaxTopDelegationsPerCandidate as Get>::get() + + <::MaxBottomDelegationsPerCandidate as Get>::get() + ); + + let candidate: T::AccountId = create_funded_collator::( + "unique_caller", + USER_SEED - 100, + 0u32.into(), + true, + 1u32, + )?; + // 2nd delegation required for all delegators to ensure DelegatorState updated not removed + let second_candidate: T::AccountId = create_funded_collator::( + "unique__caller", + USER_SEED - 99, + 0u32.into(), + true, + 2u32, + )?; + let mut delegators: Vec = Vec::new(); + let mut col_del_count = 0u32; + for (col_del_ac_count, i) in (1..x).enumerate() { + let seed = USER_SEED + i; + let delegator = create_funded_delegator::( + "delegator", + seed, + min_delegator_stk::(), + candidate.clone(), + true, + col_del_count, + )?; + Pallet::::delegate_with_auto_compound( + RawOrigin::Signed(delegator.clone()).into(), + second_candidate.clone(), + min_delegator_stk::(), + Percent::from_percent(50), + col_del_count, + col_del_ac_count as u32, + 1u32, + )?; + + Pallet::::schedule_revoke_delegation( + RawOrigin::Signed(delegator.clone()).into(), + candidate.clone() + )?; + delegators.push(delegator); + col_del_count += 1u32; + } + Pallet::::schedule_leave_candidates( + RawOrigin::Signed(candidate.clone()).into(), + 3u32 + )?; + roll_to_and_author::(<::LeaveCandidatesDelay as Get>::get(), candidate.clone()); + }: { + >::execute_leave_candidates( + RawOrigin::Signed(candidate.clone()).into(), + candidate.clone(), + col_del_count, + )?; + } + verify { + assert!(Pallet::::candidate_info(&candidate).is_none()); + assert!(Pallet::::candidate_info(&second_candidate).is_some()); + for delegator in delegators { + assert!(Pallet::::is_delegator(&delegator)); + } + } + + execute_leave_candidates_ideal { + // x is total number of delegations for the candidate + let x in 2..( + <::MaxTopDelegationsPerCandidate as Get>::get() + + <::MaxBottomDelegationsPerCandidate as Get>::get() + ); + // y is the total number of auto-compounding delegations for the candidate + let y in 2..( + <::MaxTopDelegationsPerCandidate as Get>::get() + + <::MaxBottomDelegationsPerCandidate as Get>::get() + ); + + let candidate: T::AccountId = create_funded_collator::( + "unique_caller", + USER_SEED - 100, + 0u32.into(), + true, + 1u32, + )?; + // 2nd delegation required for all delegators to ensure DelegatorState updated not removed + let second_candidate: T::AccountId = create_funded_collator::( + "unique__caller", + USER_SEED - 99, + 0u32.into(), + true, + 2u32, + )?; + let mut delegators: Vec = Vec::new(); + let mut col_del_count = 0u32; + + #[allow(clippy::explicit_counter_loop)] + for (col_del_ac_count, i) in (1..x).enumerate() { + let seed = USER_SEED + i; + let delegator = create_funded_delegator::( + "delegator", + seed, + min_delegator_stk::(), + candidate.clone(), + true, + col_del_count, + )?; + if i < y { + Pallet::::delegate_with_auto_compound( + RawOrigin::Signed(delegator.clone()).into(), + second_candidate.clone(), + min_delegator_stk::(), + Percent::from_percent(50), + col_del_count, + col_del_ac_count as u32, + 1u32, + )?; + } else { + Pallet::::delegate( + RawOrigin::Signed(delegator.clone()).into(), + second_candidate.clone(), + min_delegator_stk::(), + col_del_count, + 1u32, + )?; + } + + Pallet::::schedule_revoke_delegation( + RawOrigin::Signed(delegator.clone()).into(), + candidate.clone() + )?; + delegators.push(delegator); + col_del_count += 1u32; + } + Pallet::::schedule_leave_candidates( + RawOrigin::Signed(candidate.clone()).into(), + 3u32 + )?; + roll_to_and_author::(<::LeaveCandidatesDelay as Get>::get(), candidate.clone()); + }: { + >::execute_leave_candidates_inner(candidate.clone())?; + } + verify { + assert!(Pallet::::candidate_info(&candidate).is_none()); + assert!(Pallet::::candidate_info(&second_candidate).is_some()); + for delegator in delegators { + assert!(Pallet::::is_delegator(&delegator)); + } + } + + cancel_leave_candidates { + let x in 3..T::MaxCandidates::get(); + // Worst Case Complexity is removal from an ordered list so \exists full list before call + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + 0u32.into(), + true, + candidate_count + )?; + candidate_count += 1u32; + } + let caller: T::AccountId = create_funded_collator::( + "caller", + USER_SEED, + 0u32.into(), + true, + candidate_count, + )?; + candidate_count += 1u32; + Pallet::::schedule_leave_candidates( + RawOrigin::Signed(caller.clone()).into(), + candidate_count + )?; + candidate_count -= 1u32; + }: _(RawOrigin::Signed(caller.clone()), candidate_count) + verify { + assert!(Pallet::::candidate_info(&caller).expect("must exist").is_active()); + } + + go_offline { + let x in 1..T::MaxCandidates::get(); + + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + 0u32.into(), + true, + candidate_count + )?; + candidate_count += 1; + } + + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + 0u32.into(), + true, + candidate_count + )?; + }: { + >::go_offline(RawOrigin::Signed(caller.clone()).into())?; + } + verify { + assert!(!Pallet::::candidate_info(&caller).expect("must exist").is_active()); + } + + go_online { + let x in 1..T::MaxCandidates::get(); + + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + 0u32.into(), + true, + candidate_count + )?; + candidate_count += 1; + } + + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + 0u32.into(), + true, + candidate_count + )?; + >::go_offline(RawOrigin::Signed(caller.clone()).into())?; + }: { + >::go_online(RawOrigin::Signed(caller.clone()).into())?; + } + verify { + assert!(Pallet::::candidate_info(&caller).expect("must exist").is_active()); + } + + candidate_bond_more { + let x in 1..T::MaxCandidates::get(); + + let more = min_candidate_stk::(); + + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + more, + true, + candidate_count + )?; + candidate_count += 1; + } + + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + more, + true, + candidate_count, + )?; + }: { + >::candidate_bond_more(RawOrigin::Signed(caller.clone()).into(), more)?; + } + verify { + let expected_bond = more * 2u32.into(); + assert_eq!( + Pallet::::candidate_info(&caller).expect("candidate was created, qed").bond, + expected_bond, + ); + } + + schedule_candidate_bond_less { + let min_candidate_stk = min_candidate_stk::(); + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + min_candidate_stk, + false, + 1u32, + )?; + }: _(RawOrigin::Signed(caller.clone()), min_candidate_stk) + verify { + let state = Pallet::::candidate_info(&caller).expect("request bonded less so exists"); + assert_eq!( + state.request, + Some(CandidateBondLessRequest { + amount: min_candidate_stk, + when_executable: 1 + <::CandidateBondLessDelay as Get>::get(), + }) + ); + } + + execute_candidate_bond_less { + let x in 1..T::MaxCandidates::get(); + + let min_candidate_stk = min_candidate_stk::(); + + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + min_candidate_stk, + true, + candidate_count + )?; + candidate_count += 1; + } + + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + min_candidate_stk, + false, + candidate_count, + )?; + + Pallet::::schedule_candidate_bond_less( + RawOrigin::Signed(caller.clone()).into(), + min_candidate_stk + )?; + roll_to_and_author::(<::LeaveCandidatesDelay as Get>::get(), caller.clone()); + }: { + Pallet::::execute_candidate_bond_less( + RawOrigin::Signed(caller.clone()).into(), + caller.clone(), + )?; + } verify { + assert_eq!( + Pallet::::candidate_info(&caller).expect("candidate was created, qed").bond, + min_candidate_stk, + ); + } + + set_candidate_bond_to_zero { + let x in 1..T::MaxCandidates::get(); + + let min_candidate_stk = min_candidate_stk::(); + + let mut candidate_count = 1u32; + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + min_candidate_stk, + true, + candidate_count + )?; + candidate_count += 1; + } + + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + min_candidate_stk, + false, + candidate_count, + )?; + + roll_to_and_author::(2, caller.clone()); + }: { + Pallet::::set_candidate_bond_to_zero(&caller); + } verify { + assert!( + Pallet::::candidate_info(&caller).expect("candidate was created, qed").bond.is_zero(), + "bond should be zero" + ); + } + + cancel_candidate_bond_less { + let min_candidate_stk = min_candidate_stk::(); + let caller: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + min_candidate_stk, + false, + 1u32, + )?; + Pallet::::schedule_candidate_bond_less( + RawOrigin::Signed(caller.clone()).into(), + min_candidate_stk + )?; + }: { + Pallet::::cancel_candidate_bond_less( + RawOrigin::Signed(caller.clone()).into(), + )?; + } verify { + assert!( + Pallet::::candidate_info(&caller).expect("must exist").request.is_none() + ); + } + + delegate { + let x in 3..<::MaxDelegationsPerDelegator as Get>::get(); + let y in 2..<::MaxTopDelegationsPerCandidate as Get>::get(); + // Worst Case is full of delegations before calling `delegate` + let mut collators: Vec = Vec::new(); + // Initialize MaxDelegationsPerDelegator collator candidates + for i in 2..x { + let seed = USER_SEED - i; + let collator = create_funded_collator::( + "collator", + seed, + 0u32.into(), + true, + collators.len() as u32 + 1u32, + )?; + collators.push(collator.clone()); + } + let bond = <::MinDelegation as Get>>::get(); + let extra = if (bond * (collators.len() as u32 + 1u32).into()) > min_candidate_stk::() { + (bond * (collators.len() as u32 + 1u32).into()) - min_candidate_stk::() + } else { + 0u32.into() + }; + let (caller, _) = create_funded_user::("caller", USER_SEED, extra); + // Delegation count + let mut del_del_count = 0u32; + // Nominate MaxDelegationsPerDelegators collator candidates + for col in collators.clone() { + Pallet::::delegate( + RawOrigin::Signed(caller.clone()).into(), col, bond, 0u32, del_del_count + )?; + del_del_count += 1u32; + } + // Last collator to be delegated + let collator: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + 0u32.into(), + true, + collators.len() as u32 + 1u32, + )?; + // Worst Case Complexity is insertion into an almost full collator + let mut col_del_count = 0u32; + for i in 1..y { + let seed = USER_SEED + i; + let _ = create_funded_delegator::( + "delegator", + seed, + 0u32.into(), + collator.clone(), + true, + col_del_count, + )?; + col_del_count += 1u32; + } + }: _(RawOrigin::Signed(caller.clone()), collator, bond, col_del_count, del_del_count) + verify { + assert!(Pallet::::is_delegator(&caller)); + } + + schedule_revoke_delegation { + // x controls the number of other scheduled requests + let x in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + + let num_top = x.min(T::MaxTopDelegationsPerCandidate::get() - 1); + let num_bottom = x.saturating_sub(num_top).min(T::MaxBottomDelegationsPerCandidate::get()); + + let mut seed = Seed::new(); + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1 }, + )?; + + let mut decreasing_balance = >::new( + T::MinDelegation::get() * 2000u32.into(), + 1u32.into(), + ); + let mut col_del_count = 0u32; + for i in 0..num_top { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + let last_top_delegator_bond = decreasing_balance.take(); + let last_top_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(last_top_delegator_bond), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + assert_eq!( + >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), + 0, + ); + + for i in 0..num_bottom { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_revoke_delegation( + RawOrigin::Signed(del).into(), + collator.clone(), + )?; + } + + assert_eq!( + >::get(&collator).map(|bd| bd.delegations.len() as u32).unwrap_or_default(), + num_bottom, + ); + + }: { + Pallet::::schedule_revoke_delegation( + RawOrigin::Signed(last_top_delegator.clone()).into(), + collator.clone(), + )?; + } + verify { + let state = Pallet::::delegator_state(&last_top_delegator) + .expect("delegator must exist"); + assert_eq!( + Pallet::::delegation_scheduled_requests(&collator) + .iter() + .find(|r| r.delegator == last_top_delegator) + .cloned(), + Some(ScheduledRequest { + delegator: last_top_delegator, + when_executable: 1 + <::RevokeDelegationDelay as Get>::get(), + action: DelegationAction::Revoke(last_top_delegator_bond), + }), + ); + } + + delegator_bond_more { + // x controls the number of other scheduled requests + let x in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + + let num_top = x.min(T::MaxTopDelegationsPerCandidate::get() - 1); + let num_bottom = x.saturating_sub(num_top).min(T::MaxBottomDelegationsPerCandidate::get()); + + let mut seed = Seed::new(); + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1 }, + )?; + + let mut decreasing_balance = >::new( + T::MinDelegation::get() * 2000u32.into(), + 1u32.into(), + ); + let mut col_del_count = 0u32; + for i in 0..num_top { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + let last_top_delegator_bond = decreasing_balance.take(); + let last_top_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(last_top_delegator_bond + 2_000u32.into()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::Value(last_top_delegator_bond), + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + assert_eq!( + >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), + 0, + ); + + for i in 0..num_bottom { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + assert_eq!( + >::get(&collator).map(|bd| bd.delegations.len() as u32).unwrap_or_default(), + num_bottom, + ); + + let bond_more = 1_000u32.into(); + }: { + >::delegator_bond_more( + RawOrigin::Signed(last_top_delegator.clone()).into(), + collator.clone(), + bond_more, + )?; + } + verify { + let expected_bond = last_top_delegator_bond + bond_more; + assert_eq!( + Pallet::::delegator_state(&last_top_delegator).expect("candidate was created, qed").total, + expected_bond, + ); + } + + schedule_delegator_bond_less { + // x controls the number of other scheduled requests + let x in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + + let num_top = x.min(T::MaxTopDelegationsPerCandidate::get() - 1); + let num_bottom = x.saturating_sub(num_top).min(T::MaxBottomDelegationsPerCandidate::get()); + + let mut seed = Seed::new(); + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1 }, + )?; + + let mut decreasing_balance = >::new( + T::MinDelegation::get() * 2000u32.into(), + 1u32.into(), + ); + let mut col_del_count = 0u32; + for i in 0..num_top { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + let last_top_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + assert_eq!( + >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), + 0, + ); + + for i in 0..num_bottom { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + assert_eq!( + >::get(&collator).map(|bd| bd.delegations.len() as u32).unwrap_or_default(), + num_bottom, + ); + let bond_less = 1_000u32.into(); + }: { + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(last_top_delegator.clone()).into(), + collator.clone(), + bond_less, + )?; + } + verify { + let state = Pallet::::delegator_state(&last_top_delegator) + .expect("just request bonded less so exists"); + assert_eq!( + Pallet::::delegation_scheduled_requests(&collator) + .iter() + .find(|r| r.delegator == last_top_delegator) + .cloned(), + Some(ScheduledRequest { + delegator: last_top_delegator, + when_executable: 1 + <::RevokeDelegationDelay as Get>::get(), + action: DelegationAction::Decrease(bond_less), + }), + ); + } + + execute_revoke_delegation { + let collator: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + 0u32.into(), + true, + 1u32 + )?; + let (caller, _) = create_funded_user::("caller", USER_SEED, 0u32.into()); + let bond = <::MinDelegation as Get>>::get(); + Pallet::::delegate(RawOrigin::Signed( + caller.clone()).into(), + collator.clone(), + bond, + 0u32, + 0u32 + )?; + Pallet::::schedule_revoke_delegation(RawOrigin::Signed( + caller.clone()).into(), + collator.clone() + )?; + roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + }: { + Pallet::::execute_delegation_request( + RawOrigin::Signed(caller.clone()).into(), + caller.clone(), + collator.clone() + )?; + } verify { + assert!( + !Pallet::::is_delegator(&caller) + ); + } + + execute_delegator_revoke_delegation_worst { + // We assume delegator has auto-compound set, collator has max scheduled requests, and delegator + // will be kicked from delegator pool, and a bottom delegator will be bumped to top. + + let mut seed = Seed::new(); + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1 }, + )?; + + let mut decreasing_balance = >::new( + T::MinDelegation::get() * 2000u32.into(), + 1u32.into(), + ); + let mut col_del_count = 0u32; + for i in 0..T::MaxTopDelegationsPerCandidate::get() - 1 { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + let last_top_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_revoke_delegation( + RawOrigin::Signed(last_top_delegator.clone()).into(), + collator.clone(), + )?; + + assert_eq!( + >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), + 0, + ); + + // insert one delegator in bottom + let highest_bottom_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(highest_bottom_delegator.clone()).into(), + collator.clone(), + 5u32.into(), + )?; + for i in 1..T::MaxBottomDelegationsPerCandidate::get() { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + assert!( + >::get(&collator) + .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) + .unwrap_or_default(), + ); + roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + }: { + Pallet::::execute_delegation_request( + RawOrigin::Signed(last_top_delegator.clone()).into(), + last_top_delegator.clone(), + collator.clone() + )?; + } verify { + assert!(!Pallet::::is_delegator(&last_top_delegator)); + assert_eq!( + >::get(&collator) + .map(|bd| bd.delegations.len() as u32) + .unwrap_or_default(), + T::MaxBottomDelegationsPerCandidate::get() - 1, + ); + assert!( + >::get(&collator) + .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) + .unwrap_or_default(), + ); + } + + execute_delegator_bond_less_worst { + // We assume delegator will be kicked into bottom delegation and collator has + // max scheduled requests + let mut seed = Seed::new(); + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1 }, + )?; + + let mut decreasing_balance = >::new( + T::MinDelegation::get() * 2000u32.into(), + 1u32.into(), + ); + let mut col_del_count = 0u32; + for i in 0..T::MaxTopDelegationsPerCandidate::get() - 1 { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + let last_top_delegator_bond_less = 1_000u32.into(); + let last_top_delegator_total = decreasing_balance.take(); + let last_top_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(last_top_delegator_total), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(last_top_delegator.clone()).into(), + collator.clone(), + last_top_delegator_bond_less, + )?; + + assert_eq!( + >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), + 0, + ); + + // insert one delegator in bottom + let highest_bottom_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(highest_bottom_delegator.clone()).into(), + collator.clone(), + 5u32.into(), + )?; + for i in 1..T::MaxBottomDelegationsPerCandidate::get() { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + assert!( + >::get(&collator) + .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) + .unwrap_or_default(), + ); + roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + }: { + Pallet::::execute_delegation_request( + RawOrigin::Signed(last_top_delegator.clone()).into(), + last_top_delegator.clone(), + collator.clone() + )?; + } verify { + let expected = last_top_delegator_total - last_top_delegator_bond_less; + // See: https://github.com/freeverseio/laos/pull/533#issuecomment-2034913428 + // assert_eq!( + // Pallet::::delegator_state(&last_top_delegator).expect("candidate was created, qed").total, + // expected, + // ); + // assert!( + // >::get(&collator) + // .map(|bd| bd.delegations.iter().any(|d| d.owner == last_top_delegator)) + // .unwrap_or_default(), + // ); + // assert!( + // >::get(&collator) + // .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) + // .unwrap_or_default(), + // ); + } + + cancel_delegation_request { + // x is number other delegators with scheduled requests + let x in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + + let mut seed = Seed::new(); + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1 }, + )?; + + let mut col_del_count = 0u32; + for i in 0..x { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(T::MinDelegation::get() + 10u32.into()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + collator.clone(), + 5u32.into(), + )?; + } + + let delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(T::MinDelegation::get() + 100u32.into()), + AccountAction::Delegate{ + collator: collator.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(delegator.clone()).into(), + collator.clone(), + 5u32.into(), + )?; + roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + }: { + Pallet::::cancel_delegation_request( + RawOrigin::Signed(delegator.clone()).into(), + collator.clone() + )?; + } verify { + assert!( + !Pallet::::delegation_scheduled_requests(&collator) + .iter() + .any(|x| x.delegator == delegator) + ); + } + + // ON_INITIALIZE + + prepare_staking_payouts { + let reward_delay = <::RewardPaymentDelay as Get>::get(); + let round = reward_delay + 2u32; + let payout_round = round - reward_delay; + // may need: + // > + // > + // > + // ensure parachain bond account exists so that deposit_into_existing succeeds + >::insert(payout_round, 100); + >::insert(payout_round, min_candidate_stk::()); + + // set an account in the bond config so that we will measure the payout to it + let account = create_funded_user::( + "parachain_bond", + 0, + min_candidate_stk::(), + ).0; + >::put(ParachainBondConfig { + account, + percent: Percent::from_percent(50), + }); + + }: { Pallet::::prepare_staking_payouts(round); } + verify { + } + + get_rewardable_delegators { + let y in 0..<::MaxDelegationsPerDelegator as Get>::get(); // num delegators + + let high_inflation: Range = Range { + min: Perbill::one(), + ideal: Perbill::one(), + max: Perbill::one(), + }; + Pallet::::set_inflation(RawOrigin::Root.into(), high_inflation)?; + Pallet::::set_blocks_per_round(RawOrigin::Root.into(), 101u32)?; + Pallet::::set_total_selected(RawOrigin::Root.into(), 100u32)?; + + let collator = create_funded_collator::( + "collator", + 0, + min_candidate_stk::() * 1_000_000u32.into(), + true, + 1, + )?; + + // create delegators + for i in 0..y { + let seed = USER_SEED + i + 1; + let delegator = create_funded_delegator::( + "delegator", + seed, + min_candidate_stk::() * 1_000_000u32.into(), + collator.clone(), + true, + i, + )?; + } + + let mut _results = None; + + }: { _results = Some(Pallet::::get_rewardable_delegators(&collator)); } + verify { + let counted_delegations = _results.expect("get_rewardable_delegators returned some results"); + assert!(counted_delegations.uncounted_stake == 0u32.into()); + assert!(counted_delegations.rewardable_delegations.len() as u32 == y); + let top_delegations = >::get(collator.clone()) + .expect("delegations were set for collator through delegate() calls"); + assert!(top_delegations.delegations.len() as u32 == y); + } + + select_top_candidates { + let x in 0..50; // num collators + let y in 0..<::MaxDelegationsPerDelegator as Get>::get(); // num delegators + + let high_inflation: Range = Range { + min: Perbill::one(), + ideal: Perbill::one(), + max: Perbill::one(), + }; + Pallet::::set_inflation(RawOrigin::Root.into(), high_inflation)?; + Pallet::::set_blocks_per_round(RawOrigin::Root.into(), 101u32)?; + Pallet::::set_total_selected(RawOrigin::Root.into(), 100u32)?; + + let mut seed = USER_SEED + 1; + + for _ in 0..x { + let collator = create_funded_collator::( + "collator", + seed, + min_candidate_stk::() * 1_000_000u32.into(), + true, + 999999, + )?; + seed += 1; + + // create delegators + for _ in 0..y { + let delegator = create_funded_delegator::( + "delegator", + seed, + min_candidate_stk::() * 1_000_000u32.into(), + collator.clone(), + true, + 9999999, + )?; + seed += 1; + } + } + + }: { Pallet::::select_top_candidates(1); } + verify { + } + + pay_one_collator_reward_best { + // x controls number of delegations + let x in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + // y controls the number of auto-compounding delegations + let y in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + // z is the number of scheduled requests per collator + let z in 0..( + T::MaxTopDelegationsPerCandidate::get() + + T::MaxBottomDelegationsPerCandidate::get() - 1 + ); + + use crate::{ + DelayedPayout, DelayedPayouts, AtStake, CollatorSnapshot, BondWithAutoCompound, Points, + AwardedPts, + }; + + let mut seed = Seed::new(); + let prime_candidate = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1u32 }, + )?; + + let mut delegations = Vec::new(); + let initial_delegator_balance = T::MinDelegation::get() + 100u32.into(); + for (col_del_count, i) in (0..x).enumerate() { + let auto_compound = if i < y { Percent::from_percent(100) } else { Percent::from_percent(0) }; + let delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(initial_delegator_balance), + AccountAction::Delegate{ + collator: prime_candidate.clone(), + amount: Amount::All, + auto_compound, + collator_delegation_count: col_del_count as u32, + collator_auto_compound_delegation_count: col_del_count as u32, + }, + )?; + if i < z { + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(delegator.clone()).into(), + prime_candidate.clone(), + 5u32.into(), + )?; + } + + delegations.push(BondWithAutoCompound { + owner: delegator.clone(), + amount: initial_delegator_balance, + auto_compound, + }); + } + + let total_staked = min_candidate_stk::() + + (Into::>::into(x) * initial_delegator_balance); + let round_for_payout = 5; + >::insert(round_for_payout, DelayedPayout { + round_issuance: 1000u32.into(), + total_staking_reward: total_staked, + collator_commission: Perbill::from_rational(1u32, 100u32), + }); + + >::insert(round_for_payout, &prime_candidate, CollatorSnapshot { + bond: 1_000u32.into(), + delegations: delegations.clone(), + total: 1_000_000u32.into(), + }); + + >::insert(round_for_payout, 100); + >::insert(round_for_payout, &prime_candidate, 20); + + }: { + for BondWithAutoCompound { + owner, + amount, + auto_compound, + } in &delegations + { + >::mint_and_compound( + 100u32.into(), + *auto_compound, + prime_candidate.clone(), + owner.clone(), + ); + } + } + verify { + for BondWithAutoCompound { + owner, + amount, + auto_compound, + } in &delegations + { + assert!( + T::Currency::free_balance(owner) > initial_delegator_balance, + "delegator should have been paid in pay_one_collator_reward" + ); + } + } + + pay_one_collator_reward { + // y controls number of delegations, its maximum per collator is the max top delegations + let y in 0..<::MaxTopDelegationsPerCandidate as Get>::get(); + + // must come after 'let foo in 0..` statements for macro + use crate::{ + DelayedPayout, DelayedPayouts, AtStake, CollatorSnapshot, BondWithAutoCompound, Points, + AwardedPts, + }; + + let before_running_round_index = Pallet::::round().current; + let initial_stake_amount = min_candidate_stk::() * 1_000_000u32.into(); + + let mut total_staked = 0u32.into(); + + // initialize our single collator + let sole_collator = create_funded_collator::( + "collator", + 0, + initial_stake_amount, + true, + 1u32, + )?; + total_staked += initial_stake_amount; + + // generate funded delegator accounts + let mut delegators: Vec = Vec::new(); + for i in 0..y { + let seed = USER_SEED + i; + let delegator = create_funded_delegator::( + "delegator", + seed, + initial_stake_amount, + sole_collator.clone(), + true, + delegators.len() as u32, + )?; + delegators.push(delegator); + total_staked += initial_stake_amount; + } + + // rather than roll through rounds in order to initialize the storage we want, we set it + // directly and then call pay_one_collator_reward directly. + + let round_for_payout = 5; + >::insert(round_for_payout, DelayedPayout { + // NOTE: round_issuance is not correct here, but it doesn't seem to cause problems + round_issuance: 1000u32.into(), + total_staking_reward: total_staked, + collator_commission: Perbill::from_rational(1u32, 100u32), + }); + + let mut delegations: Vec>> = Vec::new(); + for delegator in &delegators { + delegations.push(BondWithAutoCompound { + owner: delegator.clone(), + amount: 100u32.into(), + auto_compound: Percent::zero(), + }); + } + + >::insert(round_for_payout, &sole_collator, CollatorSnapshot { + bond: 1_000u32.into(), + delegations, + total: 1_000_000u32.into(), + }); + + >::insert(round_for_payout, 100); + >::insert(round_for_payout, &sole_collator, 20); + + }: { + let round_for_payout = 5; + // TODO: this is an extra read right here (we should whitelist it?) + let payout_info = Pallet::::delayed_payouts(round_for_payout).expect("payout expected"); + let result = Pallet::::pay_one_collator_reward(round_for_payout, payout_info); + // TODO: how to keep this in scope so it can be done in verify block? + assert!(matches!(result.0, RewardPayment::Paid)); + } + verify { + // collator should have been paid + assert!( + T::Currency::free_balance(&sole_collator) > initial_stake_amount, + "collator should have been paid in pay_one_collator_reward" + ); + // nominators should have been paid + for delegator in &delegators { + assert!( + T::Currency::free_balance(delegator) > initial_stake_amount, + "delegator should have been paid in pay_one_collator_reward" + ); + } + } + + base_on_initialize { + let collator: T::AccountId = create_funded_collator::( + "collator", + USER_SEED, + 0u32.into(), + true, + 1u32 + )?; + let start = >::block_number(); + parachain_staking_on_finalize::(collator.clone()); + >::on_finalize(start); + >::set_block_number( + start + 1u32.into() + ); + let end = >::block_number(); + >::on_initialize(end); + }: { Pallet::::on_initialize(end); } + verify { + // Round transitions + assert_eq!(start + 1u32.into(), end); + } + + set_auto_compound { + // x controls number of distinct auto-compounding delegations the prime collator will have + // y controls number of distinct delegations the prime delegator will have + let x in 0..<::MaxTopDelegationsPerCandidate as Get>::get(); + let y in 0..<::MaxDelegationsPerDelegator as Get>::get(); + + use crate::auto_compound::AutoCompoundDelegations; + + let min_candidate_stake = min_candidate_stk::(); + let min_delegator_stake = min_delegator_stk::(); + let mut seed = Seed::new(); + + // initialize the prime collator + let prime_candidate = create_funded_collator::( + "collator", + seed.take(), + min_candidate_stake, + true, + 1, + )?; + + // initialize the prime delegator + let prime_delegator = create_funded_delegator::( + "delegator", + seed.take(), + min_delegator_stake * (y+1).into(), + prime_candidate.clone(), + true, + 0, + )?; + + // have x-1 distinct auto-compounding delegators delegate to prime collator + // we directly set the storage, since benchmarks don't work when the same extrinsic is + // called from within the benchmark. + let mut auto_compounding_state = >::get_storage(&prime_candidate); + for i in 1..x { + let delegator = create_funded_delegator::( + "delegator", + seed.take(), + min_delegator_stake, + prime_candidate.clone(), + true, + i, + )?; + auto_compounding_state.set_for_delegator( + delegator, + Percent::from_percent(100), + ).expect("must succeed"); + } + auto_compounding_state.set_storage(&prime_candidate); + + // delegate to y-1 distinct collators from the prime delegator + for i in 1..y { + let collator = create_funded_collator::( + "collator", + seed.take(), + min_candidate_stake, + true, + i+1, + )?; + Pallet::::delegate( + RawOrigin::Signed(prime_delegator.clone()).into(), + collator, + min_delegator_stake, + 0, + i, + )?; + } + }: { + Pallet::::set_auto_compound( + RawOrigin::Signed(prime_delegator.clone()).into(), + prime_candidate.clone(), + Percent::from_percent(50), + x, + y+1, + )?; + } + verify { + let actual_auto_compound = >::get_storage(&prime_candidate) + .get_for_delegator(&prime_delegator); + let expected_auto_compound = Some(Percent::from_percent(50)); + assert_eq!( + expected_auto_compound, + actual_auto_compound, + "delegation must have an auto-compound entry", + ); + } + + delegate_with_auto_compound { + // x controls number of distinct delegations the prime collator will have + // y controls number of distinct auto-compounding delegations the prime collator will have + // z controls number of distinct delegations the prime delegator will have + let x in 0..(<::MaxTopDelegationsPerCandidate as Get>::get() + + <::MaxBottomDelegationsPerCandidate as Get>::get()); + let y in 0..<::MaxTopDelegationsPerCandidate as Get>::get() + + <::MaxBottomDelegationsPerCandidate as Get>::get() - 1; + let z in 0..<::MaxDelegationsPerDelegator as Get>::get() - 1; + + use crate::auto_compound::AutoCompoundDelegations; + + let min_candidate_stake = min_candidate_stk::(); + let min_delegator_stake = min_delegator_stk::(); + let mut seed = Seed::new(); + + // initialize the prime collator + let prime_candidate = create_funded_collator::( + "collator", + seed.take(), + min_candidate_stake, + true, + 1, + )?; + + // initialize the future delegator + let (prime_delegator, _) = create_funded_user::( + "delegator", + seed.take(), + min_delegator_stake * (z+1).into(), + ); + + // have x-1 distinct delegators delegate to prime collator, of which y are auto-compounding. + // we can directly set the storage here. + for i in 1..x { + let delegator = create_funded_delegator::( + "delegator", + seed.take(), + min_delegator_stake, + prime_candidate.clone(), + true, + i, + )?; + if i <= y { + Pallet::::set_auto_compound( + RawOrigin::Signed(delegator.clone()).into(), + prime_candidate.clone(), + Percent::from_percent(100), + i+1, + i, + )?; + } + } + + // delegate to z-1 distinct collators from the prime delegator + for i in 1..z { + let collator = create_funded_collator::( + "collator", + seed.take(), + min_candidate_stake, + true, + i+1, + )?; + Pallet::::delegate( + RawOrigin::Signed(prime_delegator.clone()).into(), + collator, + min_delegator_stake, + 0, + i, + )?; + } + }: { + // Use a higher bond amount so that we become the top delegator to trigger worst case behavior. + Pallet::::delegate_with_auto_compound( + RawOrigin::Signed(prime_delegator.clone()).into(), + prime_candidate.clone(), + min_delegator_stake * 2u32.into(), + Percent::from_percent(50), + x, + y, + z, + )?; + } + verify { + assert!(Pallet::::is_delegator(&prime_delegator)); + let actual_auto_compound = >::get_storage(&prime_candidate) + .get_for_delegator(&prime_delegator); + let expected_auto_compound = Some(Percent::from_percent(50)); + assert_eq!( + expected_auto_compound, + actual_auto_compound, + "delegation must have an auto-compound entry", + ); + } + + delegate_with_auto_compound_worst { + // We assume that the delegation bumps the bottom-most delegator, which has its scheduled requests + // from a maxed delegation requests + use crate::auto_compound::AutoCompoundDelegations; + + let mut seed = Seed::new(); + let mut candidate_count = 1u32; + let prime_candidate = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count }, + )?; + candidate_count += 1; + + // setup max delegations on prime collator, with a bottom delegation that will be kicked and + // has scheduled requests on different collators. + let mut decreasing_balance = >::new( + T::MinDelegation::get() * 2000u32.into(), + 1u32.into(), + ); + let mut col_del_count = 0u32; + for i in 0..T::MaxTopDelegationsPerCandidate::get() { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: prime_candidate.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + prime_candidate.clone(), + 5u32.into(), + )?; + } + + for i in 0..T::MaxBottomDelegationsPerCandidate::get()-1 { + let del = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(decreasing_balance.take()), + AccountAction::Delegate{ + collator: prime_candidate.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_delegator_bond_less( + RawOrigin::Signed(del).into(), + prime_candidate.clone(), + 5u32.into(), + )?; + } + + let last_bottom_delegator_bond = decreasing_balance.take(); + let last_bottom_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value(last_bottom_delegator_bond), + AccountAction::Delegate{ + collator: prime_candidate.clone(), + amount: Amount::All, + auto_compound: Percent::from_percent(100), + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, + }, + )?; + col_del_count += 1; + + Pallet::::schedule_revoke_delegation( + RawOrigin::Signed(last_bottom_delegator.clone()).into(), + prime_candidate.clone(), + )?; + + assert_eq!( + >::get(&prime_candidate) + .map(|d| d.delegations.len() as u32) + .unwrap_or_default(), + T::MaxBottomDelegationsPerCandidate::get(), + ); + + assert!( + >::get(&prime_candidate) + .map(|bd| bd.delegations.iter().any(|d| d.owner == last_bottom_delegator)) + .unwrap_or_default(), + ); + + // initialize the future delegator + let prime_delegator = create_account::( + "delegator", + seed.take(), + AccountBalance::Value( + T::MinDelegation::get() * T::MaxDelegationsPerDelegator::get().into() * 3000u32.into(), + ), + AccountAction::None, + )?; + for i in 0..T::MaxDelegationsPerDelegator::get() - 2 { + let collator = create_account::( + "collator", + seed.take(), + AccountBalance::MinCandidateStake, + AccountAction::JoinCandidates{ amount: Amount::All, candidate_count }, + )?; + candidate_count += 1; + + Pallet::::delegate_with_auto_compound( + RawOrigin::Signed(prime_delegator.clone()).into(), + collator, + T::MinDelegation::get(), + Percent::from_percent(100), + 0u32, + 0u32, + i, + )?; + } + }: { + Pallet::::delegate_with_auto_compound( + RawOrigin::Signed(prime_delegator.clone()).into(), + prime_candidate.clone(), + last_bottom_delegator_bond + 1000u32.into(), + Percent::from_percent(50), + col_del_count, + col_del_count, + T::MaxDelegationsPerDelegator::get() - 1, + )?; + } + verify { + assert!(Pallet::::is_delegator(&prime_delegator)); + let actual_auto_compound = >::get_storage(&prime_candidate) + .get_for_delegator(&prime_delegator); + let expected_auto_compound = Some(Percent::from_percent(50)); + assert_eq!( + expected_auto_compound, + actual_auto_compound, + "delegation must have an auto-compound entry", + ); + } + + mint_collator_reward { + let mut seed = Seed::new(); + let collator = create_funded_collator::( + "collator", + seed.take(), + 0u32.into(), + true, + 1, + )?; + let original_free_balance = T::Currency::free_balance(&collator); + }: { + Pallet::::mint_collator_reward(1u32, collator.clone(), 50u32.into()) + } + verify { + assert_eq!(T::Currency::free_balance(&collator), original_free_balance + 50u32.into()); + } + + send_collator_rewards { + let mut seed = Seed::new(); + let collator = create_funded_collator::( + "collator", + seed.take(), + 0u32.into(), + true, + 1, + )?; + let original_free_balance = T::Currency::free_balance(&collator); + }: { + Pallet::::send_collator_rewards(1u32, collator.clone(), 50u32.into()) + } + verify { + assert_eq!(T::Currency::free_balance(&collator), original_free_balance + 50u32.into()); + } + + notify_inactive_collator { + use crate::{AtStake, CollatorSnapshot, AwardedPts}; + + // Blocks per-round must be greater than TotalSelected + Pallet::::set_blocks_per_round(RawOrigin::Root.into(), 101u32)?; + Pallet::::set_total_selected(RawOrigin::Root.into(), 100u32)?; + + let mut candidate_count = 1u32; + let mut seed = USER_SEED; + + // Create collators up to MaxCandidates + for i in 0..(T::MaxCandidates::get() - 3) { + seed += i; + let collator = create_funded_collator::( + "collator", + seed, + min_candidate_stk::() * 1_000_000u32.into(), + true, + candidate_count + )?; + candidate_count += 1; + } + + // Create two collators more: the one that will be marked as inactive + // and the one that will act as the caller of the extrinsic. + seed += 1; + let inactive_collator: T::AccountId = create_funded_collator::( + "collator", + seed, + min_candidate_stk::() * 1_000_000u32.into(), + true, + candidate_count + )?; + candidate_count += 1; + + seed += 1; + let caller: T::AccountId = create_funded_collator::( + "collator", + seed, + min_candidate_stk::() * 1_000_000u32.into(), + true, + candidate_count + )?; + + // Roll to round 2 and call to select_top_candidates. + // We do this to be able to have more than 66% of TotalSelected. + roll_to_and_author::(2, caller.clone()); + Pallet::::select_top_candidates(2); + + // Manually change these values for inactive_collator, + // so that it can be marked as inactive. + >::insert(1, &inactive_collator, CollatorSnapshot::default()); + >::insert(1, &inactive_collator, 0); + + >::insert(2, &inactive_collator, CollatorSnapshot::default()); + >::insert(2, &inactive_collator, 0); + + // Enable killswitch + >::set(true); + + }: _(RawOrigin::Signed(caller), inactive_collator.clone()) + verify { + assert!(!Pallet::::candidate_info(&inactive_collator).expect("must exist").is_active()); + } +} + +#[cfg(test)] +mod tests { + use crate::{benchmarks::*, mock::Test}; + use frame_support::assert_ok; + use sp_io::TestExternalities; + use sp_runtime::BuildStorage; + + pub fn new_test_ext() -> TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + TestExternalities::new(t) + } + + #[test] + fn bench_set_staking_expectations() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_staking_expectations()); + }); + } + + #[test] + fn bench_set_inflation() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_inflation()); + }); + } + + #[test] + fn bench_set_parachain_bond_account() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_parachain_bond_account()); + }); + } + + #[test] + fn bench_set_parachain_bond_reserve_percent() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_parachain_bond_reserve_percent()); + }); + } + + #[test] + fn bench_set_total_selected() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_total_selected()); + }); + } + + #[test] + fn bench_set_collator_commission() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_collator_commission()); + }); + } + + #[test] + fn bench_set_blocks_per_round() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_blocks_per_round()); + }); + } + + #[test] + fn bench_join_candidates() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_join_candidates()); + }); + } + + #[test] + fn bench_schedule_leave_candidates() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_schedule_leave_candidates()); + }); + } + + #[test] + fn bench_execute_leave_candidates() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_execute_leave_candidates_worst_case()); + }); + } + + #[test] + fn bench_cancel_leave_candidates() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_cancel_leave_candidates()); + }); + } + + #[test] + fn bench_go_offline() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_go_offline()); + }); + } + + #[test] + fn bench_go_online() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_go_online()); + }); + } + + #[test] + fn bench_candidate_bond_more() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_candidate_bond_more()); + }); + } + + #[test] + fn bench_schedule_candidate_bond_less() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_schedule_candidate_bond_less()); + }); + } + + #[test] + fn bench_execute_candidate_bond_less() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_execute_candidate_bond_less()); + }); + } + + #[test] + fn bench_set_candidate_bond_to_zero() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_set_candidate_bond_to_zero()); + }); + } + + #[test] + fn bench_cancel_candidate_bond_less() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_cancel_candidate_bond_less()); + }); + } + + #[test] + fn bench_delegate() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_delegate()); + }); + } + + #[test] + fn bench_schedule_revoke_delegation() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_schedule_revoke_delegation()); + }); + } + + #[test] + fn bench_delegator_bond_more() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_delegator_bond_more()); + }); + } + + #[test] + fn bench_schedule_delegator_bond_less() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_schedule_delegator_bond_less()); + }); + } + + #[test] + fn bench_execute_revoke_delegation() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_execute_revoke_delegation()); + }); + } + + #[test] + fn bench_execute_delegator_bond_less() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_execute_delegator_bond_less_worst()); + }); + } + + #[test] + fn bench_base_on_initialize() { + new_test_ext().execute_with(|| { + assert_ok!(Pallet::::test_benchmark_base_on_initialize()); + }); + } +} + +impl_benchmark_test_suite!(Pallet, crate::benchmarks::tests::new_test_ext(), crate::mock::Test); diff --git a/pallets/parachain-staking-old/src/delegation_requests.rs b/pallets/parachain-staking-old/src/delegation_requests.rs new file mode 100644 index 000000000..ff7eb4808 --- /dev/null +++ b/pallets/parachain-staking-old/src/delegation_requests.rs @@ -0,0 +1,532 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! Scheduled requests functionality for delegators + +use crate::{ + auto_compound::AutoCompoundDelegations, + pallet::{ + BalanceOf, CandidateInfo, Config, DelegationScheduledRequests, DelegatorState, Error, + Event, Pallet, Round, RoundIndex, Total, + }, + weights::WeightInfo, + Delegator, +}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo}, + ensure, + traits::Get, + BoundedVec, +}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::{traits::Saturating, RuntimeDebug}; + +/// An action that can be performed upon a delegation +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, PartialOrd, Ord)] +pub enum DelegationAction { + Revoke(Balance), + Decrease(Balance), +} + +impl DelegationAction { + /// Returns the wrapped amount value. + pub fn amount(&self) -> Balance { + match self { + DelegationAction::Revoke(amount) => *amount, + DelegationAction::Decrease(amount) => *amount, + } + } +} + +/// Represents a scheduled request that define a [DelegationAction]. The request is executable +/// iff the provided [RoundIndex] is achieved. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, PartialOrd, Ord)] +pub struct ScheduledRequest { + pub delegator: AccountId, + pub when_executable: RoundIndex, + pub action: DelegationAction, +} + +/// Represents a cancelled scheduled request for emitting an event. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct CancelledScheduledRequest { + pub when_executable: RoundIndex, + pub action: DelegationAction, +} + +impl From> for CancelledScheduledRequest { + fn from(request: ScheduledRequest) -> Self { + CancelledScheduledRequest { + when_executable: request.when_executable, + action: request.action, + } + } +} + +impl Pallet { + /// Schedules a [DelegationAction::Revoke] for the delegator, towards a given collator. + pub(crate) fn delegation_schedule_revoke( + collator: T::AccountId, + delegator: T::AccountId, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; + let mut scheduled_requests = >::get(&collator); + + let actual_weight = + T::WeightInfo::schedule_revoke_delegation(scheduled_requests.len() as u32); + + ensure!( + !scheduled_requests.iter().any(|req| req.delegator == delegator), + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::PendingDelegationRequestAlreadyExists.into(), + }, + ); + + let bonded_amount = state.get_bond_amount(&collator).ok_or(>::DelegationDNE)?; + let now = >::get().current; + let when = now.saturating_add(T::RevokeDelegationDelay::get()); + scheduled_requests + .try_push(ScheduledRequest { + delegator: delegator.clone(), + action: DelegationAction::Revoke(bonded_amount), + when_executable: when, + }) + .map_err(|_| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: Error::::ExceedMaxDelegationsPerDelegator.into(), + })?; + state.less_total = state.less_total.saturating_add(bonded_amount); + >::insert(collator.clone(), scheduled_requests); + >::insert(delegator.clone(), state); + + Self::deposit_event(Event::DelegationRevocationScheduled { + round: now, + delegator, + candidate: collator, + scheduled_exit: when, + }); + Ok(().into()) + } + + /// Schedules a [DelegationAction::Decrease] for the delegator, towards a given collator. + pub(crate) fn delegation_schedule_bond_decrease( + collator: T::AccountId, + delegator: T::AccountId, + decrease_amount: BalanceOf, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; + let mut scheduled_requests = >::get(&collator); + + let actual_weight = + T::WeightInfo::schedule_delegator_bond_less(scheduled_requests.len() as u32); + + ensure!( + !scheduled_requests.iter().any(|req| req.delegator == delegator), + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::PendingDelegationRequestAlreadyExists.into(), + }, + ); + + let bonded_amount = state.get_bond_amount(&collator).ok_or(DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegationDNE.into(), + })?; + ensure!( + bonded_amount > decrease_amount, + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegatorBondBelowMin.into(), + }, + ); + let new_amount: BalanceOf = bonded_amount.saturating_sub(decrease_amount); + ensure!( + new_amount >= T::MinDelegation::get(), + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegationBelowMin.into(), + }, + ); + + // Net Total is total after pending orders are executed + let net_total = state.total().saturating_sub(state.less_total); + // Net Total is always >= MinDelegation + let max_subtracted_amount = net_total.saturating_sub(T::MinDelegation::get()); + ensure!( + decrease_amount <= max_subtracted_amount, + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegatorBondBelowMin.into(), + }, + ); + + let now = >::get().current; + let when = now.saturating_add(T::RevokeDelegationDelay::get()); + scheduled_requests + .try_push(ScheduledRequest { + delegator: delegator.clone(), + action: DelegationAction::Decrease(decrease_amount), + when_executable: when, + }) + .map_err(|_| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: Error::::ExceedMaxDelegationsPerDelegator.into(), + })?; + state.less_total = state.less_total.saturating_add(decrease_amount); + >::insert(collator.clone(), scheduled_requests); + >::insert(delegator.clone(), state); + + Self::deposit_event(Event::DelegationDecreaseScheduled { + delegator, + candidate: collator, + amount_to_decrease: decrease_amount, + execute_round: when, + }); + Ok(Some(actual_weight).into()) + } + + /// Cancels the delegator's existing [ScheduledRequest] towards a given collator. + pub(crate) fn delegation_cancel_request( + collator: T::AccountId, + delegator: T::AccountId, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; + let mut scheduled_requests = >::get(&collator); + let actual_weight = + T::WeightInfo::cancel_delegation_request(scheduled_requests.len() as u32); + + let request = + Self::cancel_request_with_state(&delegator, &mut state, &mut scheduled_requests) + .ok_or(DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::PendingDelegationRequestDNE.into(), + })?; + + >::insert(collator.clone(), scheduled_requests); + >::insert(delegator.clone(), state); + + Self::deposit_event(Event::CancelledDelegationRequest { + delegator, + collator, + cancelled_request: request.into(), + }); + Ok(Some(actual_weight).into()) + } + + fn cancel_request_with_state( + delegator: &T::AccountId, + state: &mut Delegator>, + scheduled_requests: &mut BoundedVec< + ScheduledRequest>, + crate::auto_compound::AddGetOf, + >, + ) -> Option>> { + let request_idx = scheduled_requests.iter().position(|req| &req.delegator == delegator)?; + + let request = scheduled_requests.remove(request_idx); + let amount = request.action.amount(); + state.less_total = state.less_total.saturating_sub(amount); + Some(request) + } + + /// Executes the delegator's existing [ScheduledRequest] towards a given collator. + pub(crate) fn delegation_execute_scheduled_request( + collator: T::AccountId, + delegator: T::AccountId, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; + let mut scheduled_requests = >::get(&collator); + let request_idx = scheduled_requests + .iter() + .position(|req| req.delegator == delegator) + .ok_or(>::PendingDelegationRequestDNE)?; + let request = &scheduled_requests[request_idx]; + + let now = >::get().current; + ensure!(request.when_executable <= now, >::PendingDelegationRequestNotDueYet); + + match request.action { + DelegationAction::Revoke(amount) => { + let actual_weight = T::WeightInfo::execute_delegator_revoke_delegation_worst(); + + // revoking last delegation => leaving set of delegators + let leaving = if state.delegations.0.len() == 1usize { + true + } else { + ensure!( + state.total().saturating_sub(T::MinDelegation::get()) >= amount, + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegatorBondBelowMin.into(), + } + ); + false + }; + + // remove from pending requests + let amount = scheduled_requests.remove(request_idx).action.amount(); + state.less_total = state.less_total.saturating_sub(amount); + + // remove delegation from delegator state + state.rm_delegation::(&collator); + + // remove delegation from auto-compounding info + >::remove_auto_compound(&collator, &delegator); + + // remove delegation from collator state delegations + Self::delegator_leaves_candidate(collator.clone(), delegator.clone(), amount) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; + Self::deposit_event(Event::DelegationRevoked { + delegator: delegator.clone(), + candidate: collator.clone(), + unstaked_amount: amount, + }); + + >::insert(collator, scheduled_requests); + if leaving { + >::remove(&delegator); + Self::deposit_event(Event::DelegatorLeft { + delegator, + unstaked_amount: amount, + }); + } else { + >::insert(&delegator, state); + } + Ok(Some(actual_weight).into()) + }, + DelegationAction::Decrease(_) => { + let actual_weight = T::WeightInfo::execute_delegator_revoke_delegation_worst(); + + // remove from pending requests + let amount = scheduled_requests.remove(request_idx).action.amount(); + state.less_total = state.less_total.saturating_sub(amount); + + // decrease delegation + for bond in &mut state.delegations.0 { + if bond.owner == collator { + return if bond.amount > amount { + let amount_before: BalanceOf = bond.amount; + bond.amount = bond.amount.saturating_sub(amount); + let mut collator_info = >::get(&collator) + .ok_or(>::CandidateDNE) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err.into(), + })?; + + state + .total_sub_if::(amount, |total| { + let new_total: BalanceOf = total; + ensure!( + new_total >= T::MinDelegation::get(), + >::DelegationBelowMin + ); + + Ok(()) + }) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; + + // need to go into decrease_delegation + let in_top = collator_info + .decrease_delegation::( + &collator, + delegator.clone(), + amount_before, + amount, + ) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; + >::insert(&collator, collator_info); + let new_total_staked = >::get().saturating_sub(amount); + >::put(new_total_staked); + + >::insert( + collator.clone(), + scheduled_requests, + ); + >::insert(delegator.clone(), state); + Self::deposit_event(Event::DelegationDecreased { + delegator, + candidate: collator.clone(), + amount, + in_top, + }); + Ok(Some(actual_weight).into()) + } else { + // must rm entire delegation if bond.amount <= less or cancel request + Err(DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegationBelowMin.into(), + }) + }; + } + } + Err(DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegationDNE.into(), + }) + }, + } + } + + /// Removes the delegator's existing [ScheduledRequest] towards a given collator, if exists. + /// The state needs to be persisted by the caller of this function. + pub(crate) fn delegation_remove_request_with_state( + collator: &T::AccountId, + delegator: &T::AccountId, + state: &mut Delegator>, + ) { + let mut scheduled_requests = >::get(collator); + + let maybe_request_idx = + scheduled_requests.iter().position(|req| &req.delegator == delegator); + + if let Some(request_idx) = maybe_request_idx { + let request = scheduled_requests.remove(request_idx); + let amount = request.action.amount(); + state.less_total = state.less_total.saturating_sub(amount); + >::insert(collator, scheduled_requests); + } + } + + /// Returns true if a [ScheduledRequest] exists for a given delegation + pub fn delegation_request_exists(collator: &T::AccountId, delegator: &T::AccountId) -> bool { + >::get(collator) + .iter() + .any(|req| &req.delegator == delegator) + } + + /// Returns true if a [DelegationAction::Revoke] [ScheduledRequest] exists for a given + /// delegation + pub fn delegation_request_revoke_exists( + collator: &T::AccountId, + delegator: &T::AccountId, + ) -> bool { + >::get(collator).iter().any(|req| { + &req.delegator == delegator && matches!(req.action, DelegationAction::Revoke(_)) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::Test, set::OrderedSet, Bond}; + + #[test] + fn test_cancel_request_with_state_removes_request_for_correct_delegator_and_updates_state() { + let mut state = Delegator { + id: 1, + delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + total: 100, + less_total: 100, + status: crate::DelegatorStatus::Active, + }; + let mut scheduled_requests = vec![ + ScheduledRequest { + delegator: 1, + when_executable: 1, + action: DelegationAction::Revoke(100), + }, + ScheduledRequest { + delegator: 2, + when_executable: 1, + action: DelegationAction::Decrease(50), + }, + ] + .try_into() + .expect("must succeed"); + let removed_request = + >::cancel_request_with_state(&1, &mut state, &mut scheduled_requests); + + assert_eq!( + removed_request, + Some(ScheduledRequest { + delegator: 1, + when_executable: 1, + action: DelegationAction::Revoke(100), + }) + ); + assert_eq!( + scheduled_requests, + vec![ScheduledRequest { + delegator: 2, + when_executable: 1, + action: DelegationAction::Decrease(50), + },] + ); + assert_eq!( + state, + Delegator { + id: 1, + delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + total: 100, + less_total: 0, + status: crate::DelegatorStatus::Active, + } + ); + } + + #[test] + fn test_cancel_request_with_state_does_nothing_when_request_does_not_exist() { + let mut state = Delegator { + id: 1, + delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + total: 100, + less_total: 100, + status: crate::DelegatorStatus::Active, + }; + let mut scheduled_requests = vec![ScheduledRequest { + delegator: 2, + when_executable: 1, + action: DelegationAction::Decrease(50), + }] + .try_into() + .expect("must succeed"); + let removed_request = + >::cancel_request_with_state(&1, &mut state, &mut scheduled_requests); + + assert_eq!(removed_request, None,); + assert_eq!( + scheduled_requests, + vec![ScheduledRequest { + delegator: 2, + when_executable: 1, + action: DelegationAction::Decrease(50), + },] + ); + assert_eq!( + state, + Delegator { + id: 1, + delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + total: 100, + less_total: 100, + status: crate::DelegatorStatus::Active, + } + ); + } +} diff --git a/pallets/parachain-staking-old/src/inflation.rs b/pallets/parachain-staking-old/src/inflation.rs new file mode 100644 index 000000000..72f1cca01 --- /dev/null +++ b/pallets/parachain-staking-old/src/inflation.rs @@ -0,0 +1,250 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! Helper methods for computing issuance based on inflation +use crate::pallet::{BalanceOf, Config, Pallet}; +use frame_support::traits::{Currency, Get}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_runtime::{PerThing, Perbill, RuntimeDebug}; +use substrate_fixed::{transcendental::pow as floatpow, types::I64F64}; + +fn rounds_per_year() -> u32 { + let blocks_per_round = >::round().length; + T::SlotsPerYear::get() / blocks_per_round +} + +#[derive( + Eq, + PartialEq, + Clone, + Copy, + Encode, + Decode, + Default, + Deserialize, + RuntimeDebug, + MaxEncodedLen, + Serialize, + TypeInfo, +)] +pub struct Range { + pub min: T, + pub ideal: T, + pub max: T, +} + +impl Range { + pub fn is_valid(&self) -> bool { + self.max >= self.ideal && self.ideal >= self.min + } +} + +impl From for Range { + fn from(other: T) -> Range { + Range { min: other, ideal: other, max: other } + } +} +/// Convert an annual inflation to a round inflation +/// round = (1+annual)^(1/rounds_per_year) - 1 +pub fn perbill_annual_to_perbill_round( + annual: Range, + rounds_per_year: u32, +) -> Range { + let exponent = I64F64::from_num(1) / I64F64::from_num(rounds_per_year); + let annual_to_round = |annual: Perbill| -> Perbill { + let x = I64F64::from_num(annual.deconstruct()) / I64F64::from_num(Perbill::ACCURACY); + let y: I64F64 = floatpow(I64F64::from_num(1) + x, exponent) + .expect("Cannot overflow since rounds_per_year is u32 so worst case 0; QED"); + Perbill::from_parts( + ((y - I64F64::from_num(1)) * I64F64::from_num(Perbill::ACCURACY)) + .ceil() + .to_num::(), + ) + }; + Range { + min: annual_to_round(annual.min), + ideal: annual_to_round(annual.ideal), + max: annual_to_round(annual.max), + } +} + +/// Convert an annual inflation rate to a per-round inflation rate without considering compounding. +/// The calculation is simply dividing the annual rate by the number of rounds per year. +pub fn perbill_annual_to_perbill_round_simple( + annual: Range, + rounds_per_year: u32, +) -> Range { + let annual_to_round_simple = |annual: Perbill| -> Perbill { + // Convert the annual rate from Perbill to a fractional representation. + let x = I64F64::from_num(annual.deconstruct()) / I64F64::from_num(Perbill::ACCURACY); + // Divide the annual rate by the number of rounds to get the per-round rate. + let y: I64F64 = x / I64F64::from_num(rounds_per_year); + // Convert back to Perbill, rounding as necessary. + Perbill::from_parts((y * I64F64::from_num(Perbill::ACCURACY)).floor().to_num::()) + }; + Range { + min: annual_to_round_simple(annual.min), + ideal: annual_to_round_simple(annual.ideal), + max: annual_to_round_simple(annual.max), + } +} + +/// Convert annual inflation rate range to round inflation range +pub fn annual_to_round(annual: Range) -> Range { + let periods = rounds_per_year::(); + perbill_annual_to_perbill_round_simple(annual, periods) +} + +/// Compute round issuance range from round inflation range and current total issuance +pub fn round_issuance_range(round: Range) -> Range> { + let circulating = T::Currency::total_issuance(); + Range { + min: round.min * circulating, + ideal: round.ideal * circulating, + max: round.max * circulating, + } +} + +#[derive( + Eq, PartialEq, Clone, Encode, Decode, Default, Deserialize, RuntimeDebug, Serialize, TypeInfo, +)] +pub struct InflationInfo { + /// Staking expectations + pub expect: Range, + /// Annual inflation range + pub annual: Range, + /// Round inflation range + pub round: Range, +} + +impl InflationInfo { + pub fn new( + annual: Range, + expect: Range, + ) -> InflationInfo { + InflationInfo { expect, annual, round: annual_to_round::(annual) } + } + /// Set round inflation range according to input annual inflation range + pub fn set_round_from_annual(&mut self, new: Range) { + self.round = annual_to_round::(new); + } + /// Reset round inflation rate based on changes to round length + pub fn reset_round(&mut self, new_length: u32) { + let periods = T::SlotsPerYear::get() / new_length; + self.round = perbill_annual_to_perbill_round_simple(self.annual, periods); + } + /// Set staking expectations + pub fn set_expectations(&mut self, expect: Range) { + self.expect = expect; + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn mock_annual_to_round(annual: Range, rounds_per_year: u32) -> Range { + perbill_annual_to_perbill_round(annual, rounds_per_year) + } + fn mock_round_issuance_range( + // Total circulating before minting + circulating: u128, + // Round inflation range + round: Range, + ) -> Range { + Range { + min: round.min * circulating, + ideal: round.ideal * circulating, + max: round.max * circulating, + } + } + #[test] + fn simple_issuance_conversion() { + // 5% inflation for 10_000_0000 = 500,000 minted over the year + // let's assume there are 10 periods in a year + // => mint 500_000 over 10 periods => 50_000 minted per period + let expected_round_issuance_range: Range = + Range { min: 48_909, ideal: 48_909, max: 48_909 }; + let schedule = Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }; + assert_eq!( + expected_round_issuance_range, + mock_round_issuance_range(10_000_000, mock_annual_to_round(schedule, 10)) + ); + } + #[test] + fn range_issuance_conversion() { + // 3-5% inflation for 10_000_0000 = 300_000-500,000 minted over the year + // let's assume there are 10 periods in a year + // => mint 300_000-500_000 over 10 periods => 30_000-50_000 minted per period + let expected_round_issuance_range: Range = + Range { min: 29_603, ideal: 39298, max: 48_909 }; + let schedule = Range { + min: Perbill::from_percent(3), + ideal: Perbill::from_percent(4), + max: Perbill::from_percent(5), + }; + assert_eq!( + expected_round_issuance_range, + mock_round_issuance_range(10_000_000, mock_annual_to_round(schedule, 10)) + ); + } + #[test] + fn expected_parameterization() { + let expected_round_schedule: Range = Range { min: 45, ideal: 56, max: 56 }; + let schedule = Range { + min: Perbill::from_percent(4), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }; + assert_eq!( + expected_round_schedule, + mock_round_issuance_range(10_000_000, mock_annual_to_round(schedule, 8766)) + ); + } + #[test] + fn inflation_does_not_panic_at_round_number_limit() { + let schedule = Range { + min: Perbill::from_percent(100), + ideal: Perbill::from_percent(100), + max: Perbill::from_percent(100), + }; + mock_round_issuance_range(u32::MAX.into(), mock_annual_to_round(schedule, u32::MAX)); + mock_round_issuance_range(u64::MAX.into(), mock_annual_to_round(schedule, u32::MAX)); + mock_round_issuance_range(u128::MAX, mock_annual_to_round(schedule, u32::MAX)); + mock_round_issuance_range(u32::MAX.into(), mock_annual_to_round(schedule, 1)); + mock_round_issuance_range(u64::MAX.into(), mock_annual_to_round(schedule, 1)); + mock_round_issuance_range(u128::MAX, mock_annual_to_round(schedule, 1)); + } + + #[test] + fn test_use_simple_interest_per_round_calculation() { + let annual = Range { + min: Perbill::from_parts(75_000_000), + ideal: Perbill::from_parts(75_000_000), + max: Perbill::from_parts(75_000_000), + }; + assert_eq!(perbill_annual_to_perbill_round_simple(annual, 1).ideal.deconstruct(), 74999999); + assert_eq!(perbill_annual_to_perbill_round_simple(annual, 2).ideal.deconstruct(), 37499999); + assert_eq!(perbill_annual_to_perbill_round_simple(annual, 4).ideal.deconstruct(), 18749999); + assert_eq!(perbill_annual_to_perbill_round_simple(annual, 8).ideal.deconstruct(), 9374999); + assert_eq!(perbill_annual_to_perbill_round_simple(annual, 16).ideal.deconstruct(), 4687499); + } +} diff --git a/pallets/parachain-staking-old/src/lib.rs b/pallets/parachain-staking-old/src/lib.rs new file mode 100644 index 000000000..77ff0ce59 --- /dev/null +++ b/pallets/parachain-staking-old/src/lib.rs @@ -0,0 +1,2207 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! # Parachain Staking +//! Minimal staking pallet that implements collator selection by total backed stake. +//! The main difference between this pallet and `frame/pallet-staking` is that this pallet +//! uses direct delegation. Delegators choose exactly who they delegate and with what stake. +//! This is different from `frame/pallet-staking` where delegators approval vote and run Phragmen. +//! +//! ### Rules +//! There is a new round every `>::get().length` blocks. +//! +//! At the start of every round, +//! * issuance is calculated for collators (and their delegators) for block authoring +//! `T::RewardPaymentDelay` rounds ago +//! * a new set of collators is chosen from the candidates +//! +//! Immediately following a round change, payments are made once-per-block until all payments have +//! been made. In each such block, one collator is chosen for a rewards payment and is paid along +//! with each of its top `T::MaxTopDelegationsPerCandidate` delegators. +//! +//! To join the set of candidates, call `join_candidates` with `bond >= MinCandidateStk`. +//! To leave the set of candidates, call `schedule_leave_candidates`. If the call succeeds, +//! the collator is removed from the pool of candidates so they cannot be selected for future +//! collator sets, but they are not unbonded until their exit request is executed. Any signed +//! account may trigger the exit `T::LeaveCandidatesDelay` rounds after the round in which the +//! original request was made. +//! +//! To join the set of delegators, call `delegate` and pass in an account that is +//! already a collator candidate and `bond >= MinDelegation`. Each delegator can delegate up to +//! `T::MaxDelegationsPerDelegator` collator candidates by calling `delegate`. +//! +//! To revoke a delegation, call `revoke_delegation` with the collator candidate's account. +//! To leave the set of delegators and revoke all delegations, call `leave_delegators`. + +#![cfg_attr(not(feature = "std"), no_std)] + +mod auto_compound; +mod delegation_requests; +pub mod inflation; +pub mod migrations; +pub mod rewards; +pub mod traits; +pub mod types; +pub mod weights; + +#[cfg(any(test, feature = "runtime-benchmarks"))] +mod benchmarks; +#[cfg(test)] +mod mock; +mod set; +#[cfg(test)] +mod tests; + +use frame_support::pallet; +pub use inflation::{InflationInfo, Range}; +pub use weights::WeightInfo; + +pub use auto_compound::{AddGetOf, AutoCompoundConfig, AutoCompoundDelegations}; +pub use delegation_requests::{CancelledScheduledRequest, DelegationAction, ScheduledRequest}; +pub use pallet::*; +pub use traits::*; +pub use types::*; +pub use RoundIndex; + +#[pallet] +pub mod pallet { + use crate::{ + delegation_requests::{CancelledScheduledRequest, DelegationAction, ScheduledRequest}, + set::BoundedOrderedSet, + traits::*, + types::*, + AddGetOf, AutoCompoundConfig, AutoCompoundDelegations, InflationInfo, Range, WeightInfo, + }; + use frame_support::{ + fail, + pallet_prelude::*, + traits::{ + tokens::WithdrawReasons, Currency, Get, LockIdentifier, LockableCurrency, + ReservableCurrency, + }, + }; + use frame_system::pallet_prelude::*; + use sp_consensus_slots::Slot; + use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchErrorWithPostInfo, Perbill, Percent, + }; + use sp_std::{collections::btree_map::BTreeMap, prelude::*}; + + /// Pallet for parachain staking + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + pub type RoundIndex = u32; + type RewardPoint = u32; + pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + pub type DelegationOf = ( + ::AccountId, + ::AccountId, + BalanceOf, + Percent, + ); + + pub const COLLATOR_LOCK_ID: LockIdentifier = *b"stkngcol"; + pub const DELEGATOR_LOCK_ID: LockIdentifier = *b"stkngdel"; + + /// A hard limit for weight computation purposes for the max candidates that _could_ + /// theoretically exist. + pub const MAX_CANDIDATES: u32 = 200; + + /// Configuration trait of this pallet. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The currency type + type Currency: Currency + + ReservableCurrency + + LockableCurrency; + /// The origin for monetary governance + type MonetaryGovernanceOrigin: EnsureOrigin; + /// Minimum number of blocks per round + #[pallet::constant] + type MinBlocksPerRound: Get; + /// If a collator doesn't produce any block on this number of rounds, it is notified as + /// inactive. This value must be less than or equal to RewardPaymentDelay. + #[pallet::constant] + type MaxOfflineRounds: Get; + /// Number of rounds that candidates remain bonded before exit request is executable + #[pallet::constant] + type LeaveCandidatesDelay: Get; + /// Number of rounds candidate requests to decrease self-bond must wait to be executable + #[pallet::constant] + type CandidateBondLessDelay: Get; + /// Number of rounds that delegators remain bonded before exit request is executable + #[pallet::constant] + type LeaveDelegatorsDelay: Get; + /// Number of rounds that delegations remain bonded before revocation request is executable + #[pallet::constant] + type RevokeDelegationDelay: Get; + /// Number of rounds that delegation less requests must wait before executable + #[pallet::constant] + type DelegationBondLessDelay: Get; + /// Number of rounds after which block authors are rewarded + #[pallet::constant] + type RewardPaymentDelay: Get; + /// Minimum number of selected candidates every round + #[pallet::constant] + type MinSelectedCandidates: Get; + /// Maximum top delegations counted per candidate + #[pallet::constant] + type MaxTopDelegationsPerCandidate: Get; + /// Maximum bottom delegations (not counted) per candidate + #[pallet::constant] + type MaxBottomDelegationsPerCandidate: Get; + /// Maximum delegations per delegator + #[pallet::constant] + type MaxDelegationsPerDelegator: Get; + /// Minimum stake required for any account to be a collator candidate + #[pallet::constant] + type MinCandidateStk: Get>; + /// Minimum stake for any registered on-chain account to delegate + #[pallet::constant] + type MinDelegation: Get>; + /// Get the current block author + type BlockAuthor: Get; + /// Handler to notify the runtime when a collator is paid. + /// If you don't need it, you can specify the type `()`. + type OnCollatorPayout: OnCollatorPayout>; + /// Handler to distribute a collator's reward. + /// To use the default implementation of minting rewards, specify the type `()`. + type PayoutReward: PayoutReward; + /// Handler to notify the runtime when a collator is inactive. + /// The default behavior is to mark the collator as offline. + /// If you need to use the default implementation, specify the type `()`. + type OnInactiveCollator: OnInactiveCollator; + /// Handler to notify the runtime when a new round begin. + /// If you don't need it, you can specify the type `()`. + type OnNewRound: OnNewRound; + /// Get the slot number to use as clocktime for staking rounds + type SlotProvider: Get; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + /// Maximum candidates + #[pallet::constant] + type MaxCandidates: Get; + /// Average number of slots per year + /// A slot here is the unit of time for staking rounds (provided by SlotProvider) + type SlotsPerYear: Get; + } + + #[pallet::error] + pub enum Error { + DelegatorDNE, + DelegatorDNEinTopNorBottom, + DelegatorDNEInDelegatorSet, + CandidateDNE, + DelegationDNE, + DelegatorExists, + CandidateExists, + CandidateBondBelowMin, + InsufficientBalance, + DelegatorBondBelowMin, + DelegationBelowMin, + AlreadyOffline, + AlreadyActive, + DelegatorAlreadyLeaving, + DelegatorNotLeaving, + DelegatorCannotLeaveYet, + CannotDelegateIfLeaving, + CandidateAlreadyLeaving, + CandidateNotLeaving, + CandidateCannotLeaveYet, + CannotGoOnlineIfLeaving, + ExceedMaxDelegationsPerDelegator, + AlreadyDelegatedCandidate, + InvalidSchedule, + CannotSetBelowMin, + RoundLengthMustBeGreaterThanTotalSelectedCollators, + NoWritingSameValue, + TooLowCandidateCountWeightHintJoinCandidates, + TooLowCandidateCountWeightHintCancelLeaveCandidates, + TooLowCandidateCountToLeaveCandidates, + TooLowDelegationCountToDelegate, + TooLowCandidateDelegationCountToDelegate, + TooLowCandidateDelegationCountToLeaveCandidates, + TooLowDelegationCountToLeaveDelegators, + PendingCandidateRequestsDNE, + PendingCandidateRequestAlreadyExists, + PendingCandidateRequestNotDueYet, + PendingDelegationRequestDNE, + PendingDelegationRequestAlreadyExists, + PendingDelegationRequestNotDueYet, + CannotDelegateLessThanOrEqualToLowestBottomWhenFull, + PendingDelegationRevoke, + TooLowDelegationCountToAutoCompound, + TooLowCandidateAutoCompoundingDelegationCountToAutoCompound, + TooLowCandidateAutoCompoundingDelegationCountToDelegate, + TooLowCollatorCountToNotifyAsInactive, + CannotBeNotifiedAsInactive, + TooLowCandidateAutoCompoundingDelegationCountToLeaveCandidates, + TooLowCandidateCountWeightHint, + TooLowCandidateCountWeightHintGoOffline, + CandidateLimitReached, + CannotSetAboveMaxCandidates, + RemovedCall, + MarkingOfflineNotEnabled, + CurrentRoundTooLow, + DeadAccount, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// Started new round. + NewRound { + starting_block: u64, + round: RoundIndex, + selected_collators_number: u32, + total_balance: BalanceOf, + }, + /// Account joined the set of collator candidates. + JoinedCollatorCandidates { + account: T::AccountId, + amount_locked: BalanceOf, + new_total_amt_locked: BalanceOf, + }, + /// Candidate selected for collators. Total Exposed Amount includes all delegations. + CollatorChosen { + round: RoundIndex, + collator_account: T::AccountId, + total_exposed_amount: BalanceOf, + }, + /// Candidate requested to decrease a self bond. + CandidateBondLessRequested { + candidate: T::AccountId, + amount_to_decrease: BalanceOf, + execute_round: RoundIndex, + }, + /// Candidate has increased a self bond. + CandidateBondedMore { + candidate: T::AccountId, + amount: BalanceOf, + new_total_bond: BalanceOf, + }, + /// Candidate has decreased a self bond. + CandidateBondedLess { + candidate: T::AccountId, + amount: BalanceOf, + new_bond: BalanceOf, + }, + /// Candidate temporarily leave the set of collator candidates without unbonding. + CandidateWentOffline { candidate: T::AccountId }, + /// Candidate rejoins the set of collator candidates. + CandidateBackOnline { candidate: T::AccountId }, + /// Candidate has requested to leave the set of candidates. + CandidateScheduledExit { + exit_allowed_round: RoundIndex, + candidate: T::AccountId, + scheduled_exit: RoundIndex, + }, + /// Cancelled request to leave the set of candidates. + CancelledCandidateExit { candidate: T::AccountId }, + /// Cancelled request to decrease candidate's bond. + CancelledCandidateBondLess { + candidate: T::AccountId, + amount: BalanceOf, + execute_round: RoundIndex, + }, + /// Candidate has left the set of candidates. + CandidateLeft { + ex_candidate: T::AccountId, + unlocked_amount: BalanceOf, + new_total_amt_locked: BalanceOf, + }, + /// Delegator requested to decrease a bond for the collator candidate. + DelegationDecreaseScheduled { + delegator: T::AccountId, + candidate: T::AccountId, + amount_to_decrease: BalanceOf, + execute_round: RoundIndex, + }, + // Delegation increased. + DelegationIncreased { + delegator: T::AccountId, + candidate: T::AccountId, + amount: BalanceOf, + in_top: bool, + }, + // Delegation decreased. + DelegationDecreased { + delegator: T::AccountId, + candidate: T::AccountId, + amount: BalanceOf, + in_top: bool, + }, + /// Delegator requested to leave the set of delegators. + DelegatorExitScheduled { + round: RoundIndex, + delegator: T::AccountId, + scheduled_exit: RoundIndex, + }, + /// Delegator requested to revoke delegation. + DelegationRevocationScheduled { + round: RoundIndex, + delegator: T::AccountId, + candidate: T::AccountId, + scheduled_exit: RoundIndex, + }, + /// Delegator has left the set of delegators. + DelegatorLeft { delegator: T::AccountId, unstaked_amount: BalanceOf }, + /// Delegation revoked. + DelegationRevoked { + delegator: T::AccountId, + candidate: T::AccountId, + unstaked_amount: BalanceOf, + }, + /// Delegation kicked. + DelegationKicked { + delegator: T::AccountId, + candidate: T::AccountId, + unstaked_amount: BalanceOf, + }, + /// Cancelled a pending request to exit the set of delegators. + DelegatorExitCancelled { delegator: T::AccountId }, + /// Cancelled request to change an existing delegation. + CancelledDelegationRequest { + delegator: T::AccountId, + cancelled_request: CancelledScheduledRequest>, + collator: T::AccountId, + }, + /// New delegation (increase of the existing one). + Delegation { + delegator: T::AccountId, + locked_amount: BalanceOf, + candidate: T::AccountId, + delegator_position: DelegatorAdded>, + auto_compound: Percent, + }, + /// Delegation from candidate state has been remove. + DelegatorLeftCandidate { + delegator: T::AccountId, + candidate: T::AccountId, + unstaked_amount: BalanceOf, + total_candidate_staked: BalanceOf, + }, + /// Paid the account (delegator or collator) the balance as liquid rewards. + Rewarded { account: T::AccountId, rewards: BalanceOf }, + /// Transferred to account which holds funds reserved for parachain bond. + ReservedForParachainBond { account: T::AccountId, value: BalanceOf }, + /// Account (re)set for parachain bond treasury. + ParachainBondAccountSet { old: T::AccountId, new: T::AccountId }, + /// Percent of inflation reserved for parachain bond (re)set. + ParachainBondReservePercentSet { old: Percent, new: Percent }, + /// Annual inflation input (first 3) was used to derive new per-round inflation (last 3) + InflationSet { + annual_min: Perbill, + annual_ideal: Perbill, + annual_max: Perbill, + round_min: Perbill, + round_ideal: Perbill, + round_max: Perbill, + }, + /// Staking expectations set. + StakeExpectationsSet { + expect_min: BalanceOf, + expect_ideal: BalanceOf, + expect_max: BalanceOf, + }, + /// Set total selected candidates to this value. + TotalSelectedSet { old: u32, new: u32 }, + /// Set collator commission to this value. + CollatorCommissionSet { old: Perbill, new: Perbill }, + /// Set blocks per round + BlocksPerRoundSet { + current_round: RoundIndex, + first_block: u64, + old: u32, + new: u32, + new_per_round_inflation_min: Perbill, + new_per_round_inflation_ideal: Perbill, + new_per_round_inflation_max: Perbill, + }, + /// Auto-compounding reward percent was set for a delegation. + AutoCompoundSet { candidate: T::AccountId, delegator: T::AccountId, value: Percent }, + /// Compounded a portion of rewards towards the delegation. + Compounded { candidate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + let mut weight = T::WeightInfo::base_on_initialize(); + + // fetch slot number + let slot: u64 = T::SlotProvider::get().into(); + + // account for SlotProvider read + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + + let mut round = >::get(); + if round.should_update(slot) { + // mutate round + round.update(slot); + // notify that new round begin + weight = weight.saturating_add(T::OnNewRound::on_new_round(round.current)); + // pay all stakers for T::RewardPaymentDelay rounds ago + weight = weight.saturating_add(Self::prepare_staking_payouts(round.current)); + // select top collator candidates for next round + let (extra_weight, collator_count, _delegation_count, total_staked) = + Self::select_top_candidates(round.current); + weight = weight.saturating_add(extra_weight); + // start next round + >::put(round); + // snapshot total stake + >::insert(round.current, >::get()); + Self::deposit_event(Event::NewRound { + starting_block: round.first, + round: round.current, + selected_collators_number: collator_count, + total_balance: total_staked, + }); + // account for Round and Staked writes + weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 2)); + } else { + weight = weight.saturating_add(Self::handle_delayed_payouts(round.current)); + } + + // add on_finalize weight + // read: Author, Points, AwardedPts + // write: Points, AwardedPts + weight = weight.saturating_add(T::DbWeight::get().reads_writes(3, 2)); + weight + } + fn on_finalize(_n: BlockNumberFor) { + Self::award_points_to_block_author(); + } + } + + #[pallet::storage] + #[pallet::getter(fn collator_commission)] + /// Commission percent taken off of rewards for all collators + type CollatorCommission = StorageValue<_, Perbill, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn total_selected)] + /// The total candidates selected every round + pub(crate) type TotalSelected = StorageValue<_, u32, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn parachain_bond_info)] + /// Parachain bond config info { account, percent_of_inflation } + pub(crate) type ParachainBondInfo = + StorageValue<_, ParachainBondConfig, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn round)] + /// Current round index and next round scheduled transition + pub type Round = StorageValue<_, RoundInfo, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn delegator_state)] + /// Get delegator state associated with an account if account is delegating else None + pub(crate) type DelegatorState = StorageMap< + _, + Twox64Concat, + T::AccountId, + Delegator>, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn candidate_info)] + /// Get collator candidate info associated with an account if account is candidate else None + pub(crate) type CandidateInfo = + StorageMap<_, Twox64Concat, T::AccountId, CandidateMetadata>, OptionQuery>; + + pub struct AddGet { + _phantom: PhantomData<(T, R)>, + } + impl Get for AddGet + where + T: Get, + R: Get, + { + fn get() -> u32 { + T::get() + R::get() + } + } + + /// Stores outstanding delegation requests per collator. + #[pallet::storage] + #[pallet::getter(fn delegation_scheduled_requests)] + pub(crate) type DelegationScheduledRequests = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec>, AddGetOf>, + ValueQuery, + >; + + /// Stores auto-compounding configuration per collator. + #[pallet::storage] + #[pallet::getter(fn auto_compounding_delegations)] + pub(crate) type AutoCompoundingDelegations = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec, AddGetOf>, + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn top_delegations)] + /// Top delegations for collator candidate + pub(crate) type TopDelegations = StorageMap< + _, + Twox64Concat, + T::AccountId, + Delegations>, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn bottom_delegations)] + /// Bottom delegations for collator candidate + pub(crate) type BottomDelegations = StorageMap< + _, + Twox64Concat, + T::AccountId, + Delegations>, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn selected_candidates)] + /// The collator candidates selected for the current round + type SelectedCandidates = + StorageValue<_, BoundedVec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn total)] + /// Total capital locked by this staking pallet + pub(crate) type Total = StorageValue<_, BalanceOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn candidate_pool)] + /// The pool of collator candidates, each with their total backing stake + pub(crate) type CandidatePool = StorageValue< + _, + BoundedOrderedSet>, T::MaxCandidates>, + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn at_stake)] + /// Snapshot of collator delegation stake at the start of the round + pub type AtStake = StorageDoubleMap< + _, + Twox64Concat, + RoundIndex, + Twox64Concat, + T::AccountId, + CollatorSnapshot>, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn delayed_payouts)] + /// Delayed payouts + pub type DelayedPayouts = + StorageMap<_, Twox64Concat, RoundIndex, DelayedPayout>, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn staked)] + /// Total counted stake for selected candidates in the round + pub type Staked = StorageMap<_, Twox64Concat, RoundIndex, BalanceOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn inflation_config)] + /// Inflation configuration + pub type InflationConfig = StorageValue<_, InflationInfo>, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn points)] + /// Total points awarded to collators for block production in the round + pub type Points = StorageMap<_, Twox64Concat, RoundIndex, RewardPoint, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn awarded_pts)] + /// Points for each collator per round + pub type AwardedPts = StorageDoubleMap< + _, + Twox64Concat, + RoundIndex, + Twox64Concat, + T::AccountId, + RewardPoint, + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn marking_offline)] + /// Killswitch to enable/disable marking offline feature. + pub type EnableMarkingOffline = StorageValue<_, bool, ValueQuery>; + + /// Source of rewards for block producers + #[pallet::storage] + #[pallet::getter(fn rewards_account)] + pub type RewardsAccount = StorageValue<_, T::AccountId, OptionQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + /// Initialize balance and register all as collators: `(collator AccountId, balance + /// Amount)` + pub candidates: Vec<(T::AccountId, BalanceOf)>, + /// Initialize balance and make delegations: + /// `(delegator AccountId, collator AccountId, delegation Amount, auto-compounding + /// Percent)` + pub delegations: Vec>, + /// Inflation configuration + pub inflation_config: InflationInfo>, + /// Default fixed percent a collator takes off the top of due rewards + pub collator_commission: Perbill, + /// Default percent of inflation set aside for parachain bond every round + pub parachain_bond_reserve_percent: Percent, + /// Default number of blocks in a round + pub blocks_per_round: u32, + /// Number of selected candidates every round. Cannot be lower than MinSelectedCandidates + pub num_selected_candidates: u32, + /// Source of rewards for block producers + pub rewards_account: Option, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { + candidates: vec![], + delegations: vec![], + inflation_config: Default::default(), + collator_commission: Default::default(), + parachain_bond_reserve_percent: Default::default(), + blocks_per_round: 1u32, + num_selected_candidates: T::MinSelectedCandidates::get(), + rewards_account: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + RewardsAccount::::set(self.rewards_account.clone()); + + assert!(self.blocks_per_round > 0, "Blocks per round must be > 0"); + + let mut candidate_count = 0u32; + // Initialize the candidates + for &(ref candidate, balance) in &self.candidates { + assert!( + >::get_collator_stakable_free_balance(candidate) >= balance, + "Account does not have enough balance to bond as a candidate." + ); + if let Err(error) = >::join_candidates( + T::RuntimeOrigin::from(Some(candidate.clone()).into()), + balance, + candidate_count, + ) { + log::warn!("Join candidates failed in genesis with error {:?}", error); + } else { + candidate_count = candidate_count.saturating_add(1u32); + } + } + + let mut col_delegator_count: BTreeMap = BTreeMap::new(); + let mut col_auto_compound_delegator_count: BTreeMap = + BTreeMap::new(); + let mut del_delegation_count: BTreeMap = BTreeMap::new(); + // Initialize the delegations + for &(ref delegator, ref target, balance, auto_compound) in &self.delegations { + assert!( + >::get_delegator_stakable_free_balance(delegator) >= balance, + "Account does not have enough balance to place delegation." + ); + let cd_count = + if let Some(x) = col_delegator_count.get(target) { *x } else { 0u32 }; + let dd_count = + if let Some(x) = del_delegation_count.get(delegator) { *x } else { 0u32 }; + let cd_auto_compound_count = + col_auto_compound_delegator_count.get(target).cloned().unwrap_or_default(); + if let Err(error) = >::delegate_with_auto_compound( + T::RuntimeOrigin::from(Some(delegator.clone()).into()), + target.clone(), + balance, + auto_compound, + cd_count, + cd_auto_compound_count, + dd_count, + ) { + log::warn!("Delegate failed in genesis with error {:?}", error); + } else { + if let Some(x) = col_delegator_count.get_mut(target) { + *x = x.saturating_add(1u32); + } else { + col_delegator_count.insert(target.clone(), 1u32); + }; + if let Some(x) = del_delegation_count.get_mut(delegator) { + *x = x.saturating_add(1u32); + } else { + del_delegation_count.insert(delegator.clone(), 1u32); + }; + if !auto_compound.is_zero() { + col_auto_compound_delegator_count + .entry(target.clone()) + .and_modify(|x| *x = x.saturating_add(1)) + .or_insert(1); + } + } + } + // Set collator commission to default config + >::put(self.collator_commission); + // Set parachain bond config to default config + >::put(ParachainBondConfig { + // must be set soon; if not => due inflation will be sent to collators/delegators + account: T::AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"), + percent: self.parachain_bond_reserve_percent, + }); + // Set total selected candidates to value from config + assert!( + self.num_selected_candidates >= T::MinSelectedCandidates::get(), + "{:?}", + Error::::CannotSetBelowMin + ); + assert!( + self.num_selected_candidates <= T::MaxCandidates::get(), + "{:?}", + Error::::CannotSetAboveMaxCandidates + ); + >::put(self.num_selected_candidates); + // Choose top TotalSelected collator candidates + let (_, v_count, _, total_staked) = >::select_top_candidates(1u32); + // Start Round 1 at Block 0 + let round: RoundInfo = RoundInfo::new(1u32, 0u64, self.blocks_per_round); + >::put(round); + + // Set inflation configuration + let mut inflation_config = self.inflation_config.clone(); + // if all the round values are 0, derive them from the annual values + if inflation_config.round.min.is_zero() && + inflation_config.round.ideal.is_zero() && + inflation_config.round.max.is_zero() + { + inflation_config.set_round_from_annual::(inflation_config.annual); + } + >::put(inflation_config); + + // Snapshot total stake + >::insert(1u32, >::get()); + >::deposit_event(Event::NewRound { + starting_block: u64::default(), + round: 1u32, + selected_collators_number: v_count, + total_balance: total_staked, + }); + } + } + + #[pallet::call] + impl Pallet { + /// Set the expectations for total staked. These expectations determine the issuance for + /// the round according to logic in `fn compute_issuance` + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn set_staking_expectations( + origin: OriginFor, + expectations: Range>, + ) -> DispatchResultWithPostInfo { + T::MonetaryGovernanceOrigin::ensure_origin(origin)?; + ensure!(expectations.is_valid(), Error::::InvalidSchedule); + let mut config = >::get(); + ensure!(config.expect != expectations, Error::::NoWritingSameValue); + config.set_expectations(expectations); + Self::deposit_event(Event::StakeExpectationsSet { + expect_min: config.expect.min, + expect_ideal: config.expect.ideal, + expect_max: config.expect.max, + }); + >::put(config); + Ok(().into()) + } + + /// Set the annual inflation rate to derive per-round inflation + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::set_inflation())] + pub fn set_inflation( + origin: OriginFor, + schedule: Range, + ) -> DispatchResultWithPostInfo { + T::MonetaryGovernanceOrigin::ensure_origin(origin)?; + ensure!(schedule.is_valid(), Error::::InvalidSchedule); + let mut config = >::get(); + ensure!(config.annual != schedule, Error::::NoWritingSameValue); + config.annual = schedule; + config.set_round_from_annual::(schedule); + Self::deposit_event(Event::InflationSet { + annual_min: config.annual.min, + annual_ideal: config.annual.ideal, + annual_max: config.annual.max, + round_min: config.round.min, + round_ideal: config.round.ideal, + round_max: config.round.max, + }); + >::put(config); + Ok(().into()) + } + + /// Set the account that will hold funds set aside for parachain bond + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::set_parachain_bond_account())] + pub fn set_parachain_bond_account( + origin: OriginFor, + new: T::AccountId, + ) -> DispatchResultWithPostInfo { + T::MonetaryGovernanceOrigin::ensure_origin(origin)?; + let ParachainBondConfig { account: old, percent } = >::get(); + ensure!(old != new, Error::::NoWritingSameValue); + >::put(ParachainBondConfig { account: new.clone(), percent }); + Self::deposit_event(Event::ParachainBondAccountSet { old, new }); + Ok(().into()) + } + + /// Set the percent of inflation set aside for parachain bond + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::set_parachain_bond_reserve_percent())] + pub fn set_parachain_bond_reserve_percent( + origin: OriginFor, + new: Percent, + ) -> DispatchResultWithPostInfo { + T::MonetaryGovernanceOrigin::ensure_origin(origin)?; + let ParachainBondConfig { account, percent: old } = >::get(); + ensure!(old != new, Error::::NoWritingSameValue); + >::put(ParachainBondConfig { account, percent: new }); + Self::deposit_event(Event::ParachainBondReservePercentSet { old, new }); + Ok(().into()) + } + + /// Set the total number of collator candidates selected per round + /// - changes are not applied until the start of the next round + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::set_total_selected())] + pub fn set_total_selected(origin: OriginFor, new: u32) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + ensure!(new >= T::MinSelectedCandidates::get(), Error::::CannotSetBelowMin); + ensure!(new <= T::MaxCandidates::get(), Error::::CannotSetAboveMaxCandidates); + let old = >::get(); + ensure!(old != new, Error::::NoWritingSameValue); + ensure!( + new < >::get().length, + Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, + ); + >::put(new); + Self::deposit_event(Event::TotalSelectedSet { old, new }); + Ok(().into()) + } + + /// Set the commission for all collators + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::set_collator_commission())] + pub fn set_collator_commission( + origin: OriginFor, + new: Perbill, + ) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + let old = >::get(); + ensure!(old != new, Error::::NoWritingSameValue); + >::put(new); + Self::deposit_event(Event::CollatorCommissionSet { old, new }); + Ok(().into()) + } + + /// Set blocks per round + /// - if called with `new` less than length of current round, will transition immediately + /// in the next block + /// - also updates per-round inflation config + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::set_blocks_per_round())] + pub fn set_blocks_per_round(origin: OriginFor, new: u32) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + ensure!(new >= T::MinBlocksPerRound::get(), Error::::CannotSetBelowMin); + let mut round = >::get(); + let (now, first, old) = (round.current, round.first, round.length); + ensure!(old != new, Error::::NoWritingSameValue); + ensure!( + new > >::get(), + Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, + ); + round.length = new; + // update per-round inflation given new rounds per year + let mut inflation_config = >::get(); + inflation_config.reset_round::(new); + >::put(round); + Self::deposit_event(Event::BlocksPerRoundSet { + current_round: now, + first_block: first, + old, + new, + new_per_round_inflation_min: inflation_config.round.min, + new_per_round_inflation_ideal: inflation_config.round.ideal, + new_per_round_inflation_max: inflation_config.round.max, + }); + >::put(inflation_config); + Ok(().into()) + } + + /// Join the set of collator candidates + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::join_candidates(*candidate_count))] + pub fn join_candidates( + origin: OriginFor, + bond: BalanceOf, + candidate_count: u32, + ) -> DispatchResultWithPostInfo { + let acc = ensure_signed(origin)?; + ensure!(bond >= T::MinCandidateStk::get(), Error::::CandidateBondBelowMin); + Self::join_candidates_inner(acc, bond, candidate_count) + } + + /// Request to leave the set of candidates. If successful, the account is immediately + /// removed from the candidate pool to prevent selection as a collator. + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::schedule_leave_candidates(*candidate_count))] + pub fn schedule_leave_candidates( + origin: OriginFor, + candidate_count: u32, + ) -> DispatchResultWithPostInfo { + let collator = ensure_signed(origin)?; + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let (now, when) = state.schedule_leave::()?; + let mut candidates = >::get(); + ensure!( + candidate_count >= candidates.0.len() as u32, + Error::::TooLowCandidateCountToLeaveCandidates + ); + if candidates.remove(&Bond::from_owner(collator.clone())) { + >::put(candidates); + } + >::insert(&collator, state); + Self::deposit_event(Event::CandidateScheduledExit { + exit_allowed_round: now, + candidate: collator, + scheduled_exit: when, + }); + Ok(().into()) + } + + /// Execute leave candidates request + #[pallet::call_index(9)] + #[pallet::weight( + ::WeightInfo::execute_leave_candidates_worst_case(*candidate_delegation_count) + )] + pub fn execute_leave_candidates( + origin: OriginFor, + candidate: T::AccountId, + candidate_delegation_count: u32, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + let state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; + ensure!( + state.delegation_count <= candidate_delegation_count, + Error::::TooLowCandidateDelegationCountToLeaveCandidates + ); + >::execute_leave_candidates_inner(candidate) + } + + /// Cancel open request to leave candidates + /// - only callable by collator account + /// - result upon successful call is the candidate is active in the candidate pool + #[pallet::call_index(10)] + #[pallet::weight(::WeightInfo::cancel_leave_candidates(*candidate_count))] + pub fn cancel_leave_candidates( + origin: OriginFor, + candidate_count: u32, + ) -> DispatchResultWithPostInfo { + let collator = ensure_signed(origin)?; + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + ensure!(state.is_leaving(), Error::::CandidateNotLeaving); + state.go_online(); + let mut candidates = >::get(); + ensure!( + candidates.0.len() as u32 <= candidate_count, + Error::::TooLowCandidateCountWeightHintCancelLeaveCandidates + ); + let maybe_inserted_candidate = candidates + .try_insert(Bond { owner: collator.clone(), amount: state.total_counted }) + .map_err(|_| Error::::CandidateLimitReached)?; + ensure!(maybe_inserted_candidate, Error::::AlreadyActive); + >::put(candidates); + >::insert(&collator, state); + Self::deposit_event(Event::CancelledCandidateExit { candidate: collator }); + Ok(().into()) + } + + /// Temporarily leave the set of collator candidates without unbonding + #[pallet::call_index(11)] + #[pallet::weight(::WeightInfo::go_offline(MAX_CANDIDATES))] + pub fn go_offline(origin: OriginFor) -> DispatchResultWithPostInfo { + let collator = ensure_signed(origin)?; + >::go_offline_inner(collator) + } + + /// Rejoin the set of collator candidates if previously had called `go_offline` + #[pallet::call_index(12)] + #[pallet::weight(::WeightInfo::go_online(MAX_CANDIDATES))] + pub fn go_online(origin: OriginFor) -> DispatchResultWithPostInfo { + let collator = ensure_signed(origin)?; + >::go_online_inner(collator) + } + + /// Increase collator candidate self bond by `more` + #[pallet::call_index(13)] + #[pallet::weight(::WeightInfo::candidate_bond_more(MAX_CANDIDATES))] + pub fn candidate_bond_more( + origin: OriginFor, + more: BalanceOf, + ) -> DispatchResultWithPostInfo { + let candidate = ensure_signed(origin)?; + >::candidate_bond_more_inner(candidate, more) + } + + /// Request by collator candidate to decrease self bond by `less` + #[pallet::call_index(14)] + #[pallet::weight(::WeightInfo::schedule_candidate_bond_less())] + pub fn schedule_candidate_bond_less( + origin: OriginFor, + less: BalanceOf, + ) -> DispatchResultWithPostInfo { + let collator = ensure_signed(origin)?; + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let when = state.schedule_bond_less::(less)?; + >::insert(&collator, state); + Self::deposit_event(Event::CandidateBondLessRequested { + candidate: collator, + amount_to_decrease: less, + execute_round: when, + }); + Ok(().into()) + } + + /// Execute pending request to adjust the collator candidate self bond + #[pallet::call_index(15)] + #[pallet::weight(::WeightInfo::execute_candidate_bond_less(MAX_CANDIDATES))] + pub fn execute_candidate_bond_less( + origin: OriginFor, + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; // we may want to reward this if caller != candidate + >::execute_candidate_bond_less_inner(candidate) + } + + /// Cancel pending request to adjust the collator candidate self bond + #[pallet::call_index(16)] + #[pallet::weight(::WeightInfo::cancel_candidate_bond_less())] + pub fn cancel_candidate_bond_less(origin: OriginFor) -> DispatchResultWithPostInfo { + let collator = ensure_signed(origin)?; + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + state.cancel_bond_less::(collator.clone())?; + >::insert(&collator, state); + Ok(().into()) + } + + /// DEPRECATED use delegateWithAutoCompound + /// If caller is not a delegator and not a collator, then join the set of delegators + /// If caller is a delegator, then makes delegation to change their delegation state + #[pallet::call_index(17)] + #[pallet::weight( + ::WeightInfo::delegate_with_auto_compound_worst() + )] + pub fn delegate( + origin: OriginFor, + candidate: T::AccountId, + amount: BalanceOf, + candidate_delegation_count: u32, + delegation_count: u32, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + >::delegate_with_auto_compound( + candidate, + delegator, + amount, + Percent::zero(), + candidate_delegation_count, + 0, + delegation_count, + ) + } + + /// If caller is not a delegator and not a collator, then join the set of delegators + /// If caller is a delegator, then makes delegation to change their delegation state + /// Sets the auto-compound config for the delegation + #[pallet::call_index(18)] + #[pallet::weight( + ::WeightInfo::delegate_with_auto_compound( + *candidate_delegation_count, + *candidate_auto_compounding_delegation_count, + *delegation_count, + ) + )] + pub fn delegate_with_auto_compound( + origin: OriginFor, + candidate: T::AccountId, + amount: BalanceOf, + auto_compound: Percent, + candidate_delegation_count: u32, + candidate_auto_compounding_delegation_count: u32, + delegation_count: u32, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + >::delegate_with_auto_compound( + candidate, + delegator, + amount, + auto_compound, + candidate_delegation_count, + candidate_auto_compounding_delegation_count, + delegation_count, + ) + } + + /// REMOVED, was schedule_leave_delegators + #[pallet::call_index(19)] + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn removed_call_19(_origin: OriginFor) -> DispatchResultWithPostInfo { + fail!(Error::::RemovedCall) + } + + /// REMOVED, was execute_leave_delegators + #[pallet::call_index(20)] + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn removed_call_20(_origin: OriginFor) -> DispatchResultWithPostInfo { + fail!(Error::::RemovedCall) + } + + /// REMOVED, was cancel_leave_delegators + #[pallet::call_index(21)] + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn removed_call_21(_origin: OriginFor) -> DispatchResultWithPostInfo { + fail!(Error::::RemovedCall) + } + + /// Request to revoke an existing delegation. If successful, the delegation is scheduled + /// to be allowed to be revoked via the `execute_delegation_request` extrinsic. + /// The delegation receives no rewards for the rounds while a revoke is pending. + /// A revoke may not be performed if any other scheduled request is pending. + #[pallet::call_index(22)] + #[pallet::weight(::WeightInfo::schedule_revoke_delegation( + T::MaxTopDelegationsPerCandidate::get() + T::MaxBottomDelegationsPerCandidate::get() + ))] + pub fn schedule_revoke_delegation( + origin: OriginFor, + collator: T::AccountId, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + Self::delegation_schedule_revoke(collator, delegator) + } + + /// Bond more for delegators wrt a specific collator candidate. + #[pallet::call_index(23)] + #[pallet::weight(::WeightInfo::delegator_bond_more( + T::MaxTopDelegationsPerCandidate::get() + T::MaxBottomDelegationsPerCandidate::get() + ))] + pub fn delegator_bond_more( + origin: OriginFor, + candidate: T::AccountId, + more: BalanceOf, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + let (in_top, weight) = Self::delegation_bond_more_without_event( + delegator.clone(), + candidate.clone(), + more, + )?; + Pallet::::deposit_event(Event::DelegationIncreased { + delegator, + candidate, + amount: more, + in_top, + }); + + Ok(Some(weight).into()) + } + + /// Request bond less for delegators wrt a specific collator candidate. The delegation's + /// rewards for rounds while the request is pending use the reduced bonded amount. + /// A bond less may not be performed if any other scheduled request is pending. + #[pallet::call_index(24)] + #[pallet::weight(T::WeightInfo::schedule_delegator_bond_less( + T::MaxTopDelegationsPerCandidate::get() + T::MaxBottomDelegationsPerCandidate::get() + ))] + pub fn schedule_delegator_bond_less( + origin: OriginFor, + candidate: T::AccountId, + less: BalanceOf, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + Self::delegation_schedule_bond_decrease(candidate, delegator, less) + } + + /// Execute pending request to change an existing delegation + #[pallet::call_index(25)] + #[pallet::weight(::WeightInfo::execute_delegator_revoke_delegation_worst())] + pub fn execute_delegation_request( + origin: OriginFor, + delegator: T::AccountId, + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; // we may want to reward caller if caller != delegator + Self::delegation_execute_scheduled_request(candidate, delegator) + } + + /// Cancel request to change an existing delegation. + #[pallet::call_index(26)] + #[pallet::weight(::WeightInfo::cancel_delegation_request(350))] + pub fn cancel_delegation_request( + origin: OriginFor, + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + Self::delegation_cancel_request(candidate, delegator) + } + + /// Sets the auto-compounding reward percentage for a delegation. + #[pallet::call_index(27)] + #[pallet::weight(::WeightInfo::set_auto_compound( + *candidate_auto_compounding_delegation_count_hint, + *delegation_count_hint, + ))] + pub fn set_auto_compound( + origin: OriginFor, + candidate: T::AccountId, + value: Percent, + candidate_auto_compounding_delegation_count_hint: u32, + delegation_count_hint: u32, + ) -> DispatchResultWithPostInfo { + let delegator = ensure_signed(origin)?; + >::set_auto_compound( + candidate, + delegator, + value, + candidate_auto_compounding_delegation_count_hint, + delegation_count_hint, + ) + } + + /// Hotfix to remove existing empty entries for candidates that have left. + #[pallet::call_index(28)] + #[pallet::weight( + T::DbWeight::get().reads_writes(2 * candidates.len() as u64, candidates.len() as u64) + )] + pub fn hotfix_remove_delegation_requests_exited_candidates( + origin: OriginFor, + candidates: Vec, + ) -> DispatchResult { + ensure_signed(origin)?; + ensure!(candidates.len() < 100, >::InsufficientBalance); + for candidate in &candidates { + ensure!( + >::get(candidate).is_none(), + >::CandidateNotLeaving + ); + ensure!( + >::get(candidate).is_empty(), + >::CandidateNotLeaving + ); + } + + for candidate in candidates { + >::remove(candidate); + } + + Ok(()) + } + + /// Notify a collator is inactive during MaxOfflineRounds + #[pallet::call_index(29)] + #[pallet::weight(::WeightInfo::notify_inactive_collator())] + pub fn notify_inactive_collator( + origin: OriginFor, + collator: T::AccountId, + ) -> DispatchResult { + ensure!(>::get(), >::MarkingOfflineNotEnabled); + ensure_signed(origin)?; + + let mut collators_len = 0usize; + let max_collators = >::get(); + + if let Some(len) = >::decode_len() { + collators_len = len; + }; + + // Check collators length is not below or eq to 66% of max_collators. + // We use saturating logic here with (2/3) + // as it is dangerous to use floating point numbers directly. + ensure!( + collators_len * 3 > (max_collators * 2) as usize, + >::TooLowCollatorCountToNotifyAsInactive + ); + + let round_info = >::get(); + let max_offline_rounds = T::MaxOfflineRounds::get(); + + ensure!(round_info.current > max_offline_rounds, >::CurrentRoundTooLow); + + // Have rounds_to_check = [8,9] + // in case we are in round 10 for instance + // with MaxOfflineRounds = 2 + let first_round_to_check = round_info.current.saturating_sub(max_offline_rounds); + let rounds_to_check = first_round_to_check..round_info.current; + + // If this counter is eq to max_offline_rounds, + // the collator should be notified as inactive + let mut inactive_counter: RoundIndex = 0u32; + + // Iter rounds to check + // + // - The collator has AtStake associated and their AwardedPts are zero + // + // If the previous condition is met in all rounds of rounds_to_check, + // the collator is notified as inactive + for r in rounds_to_check { + let stake = >::get(r, &collator); + let pts = >::get(r, &collator); + + if stake.is_some() && pts.is_zero() { + inactive_counter = inactive_counter.saturating_add(1); + } + } + + if inactive_counter == max_offline_rounds { + let _ = T::OnInactiveCollator::on_inactive_collator( + collator.clone(), + round_info.current.saturating_sub(1), + ); + } else { + return Err(>::CannotBeNotifiedAsInactive.into()); + } + + Ok(()) + } + + /// Enable/Disable marking offline feature + #[pallet::call_index(30)] + #[pallet::weight( + Weight::from_parts(3_000_000u64, 4_000u64) + .saturating_add(T::DbWeight::get().writes(1u64)) + )] + pub fn enable_marking_offline(origin: OriginFor, value: bool) -> DispatchResult { + ensure_root(origin)?; + >::set(value); + Ok(()) + } + + /// Force join the set of collator candidates. + /// It will skip the minimum required bond check. + #[pallet::call_index(31)] + #[pallet::weight(::WeightInfo::join_candidates(*candidate_count))] + pub fn force_join_candidates( + origin: OriginFor, + account: T::AccountId, + bond: BalanceOf, + candidate_count: u32, + ) -> DispatchResultWithPostInfo { + T::MonetaryGovernanceOrigin::ensure_origin(origin.clone())?; + Self::join_candidates_inner(account, bond, candidate_count) + } + } + + /// Represents a payout made via `pay_one_collator_reward`. + pub(crate) enum RewardPayment { + /// A collator was paid + Paid, + /// A collator was skipped for payment. This can happen if they haven't been awarded any + /// points, that is, they did not produce any blocks. + Skipped, + /// All collator payments have been processed. + Finished, + } + + impl Pallet { + pub fn set_candidate_bond_to_zero(acc: &T::AccountId) -> Weight { + let actual_weight = T::WeightInfo::set_candidate_bond_to_zero(T::MaxCandidates::get()); + if let Some(mut state) = >::get(acc) { + state.bond_less::(acc.clone(), state.bond); + >::insert(acc, state); + } + actual_weight + } + + pub fn is_delegator(acc: &T::AccountId) -> bool { + >::get(acc).is_some() + } + + pub fn is_candidate(acc: &T::AccountId) -> bool { + >::get(acc).is_some() + } + + pub fn is_selected_candidate(acc: &T::AccountId) -> bool { + >::get().binary_search(acc).is_ok() + } + + pub fn join_candidates_inner( + acc: T::AccountId, + bond: BalanceOf, + candidate_count: u32, + ) -> DispatchResultWithPostInfo { + ensure!(!Self::is_candidate(&acc), Error::::CandidateExists); + ensure!(!Self::is_delegator(&acc), Error::::DelegatorExists); + let mut candidates = >::get(); + let old_count = candidates.0.len() as u32; + ensure!( + candidate_count >= old_count, + Error::::TooLowCandidateCountWeightHintJoinCandidates + ); + let maybe_inserted_candidate = candidates + .try_insert(Bond { owner: acc.clone(), amount: bond }) + .map_err(|_| Error::::CandidateLimitReached)?; + ensure!(maybe_inserted_candidate, Error::::CandidateExists); + + ensure!( + Self::get_collator_stakable_free_balance(&acc) >= bond, + Error::::InsufficientBalance, + ); + T::Currency::set_lock(COLLATOR_LOCK_ID, &acc, bond, WithdrawReasons::all()); + let candidate = CandidateMetadata::new(bond); + >::insert(&acc, candidate); + let empty_delegations: Delegations> = Default::default(); + // insert empty top delegations + >::insert(&acc, empty_delegations.clone()); + // insert empty bottom delegations + >::insert(&acc, empty_delegations); + >::put(candidates); + let new_total = >::get().saturating_add(bond); + >::put(new_total); + Self::deposit_event(Event::JoinedCollatorCandidates { + account: acc, + amount_locked: bond, + new_total_amt_locked: new_total, + }); + Ok(().into()) + } + + pub fn go_offline_inner(collator: T::AccountId) -> DispatchResultWithPostInfo { + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let mut candidates = >::get(); + let actual_weight = T::WeightInfo::go_offline(candidates.0.len() as u32); + + ensure!( + state.is_active(), + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::AlreadyOffline.into(), + } + ); + state.go_offline(); + + if candidates.remove(&Bond::from_owner(collator.clone())) { + >::put(candidates); + } + >::insert(&collator, state); + Self::deposit_event(Event::CandidateWentOffline { candidate: collator }); + Ok(Some(actual_weight).into()) + } + + pub fn go_online_inner(collator: T::AccountId) -> DispatchResultWithPostInfo { + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let mut candidates = >::get(); + let actual_weight = T::WeightInfo::go_online(candidates.0.len() as u32); + + ensure!( + !state.is_active(), + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::AlreadyActive.into(), + } + ); + ensure!( + !state.is_leaving(), + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::CannotGoOnlineIfLeaving.into(), + } + ); + state.go_online(); + + let maybe_inserted_candidate = candidates + .try_insert(Bond { owner: collator.clone(), amount: state.total_counted }) + .map_err(|_| Error::::CandidateLimitReached)?; + ensure!( + maybe_inserted_candidate, + DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::AlreadyActive.into(), + }, + ); + + >::put(candidates); + >::insert(&collator, state); + Self::deposit_event(Event::CandidateBackOnline { candidate: collator }); + Ok(Some(actual_weight).into()) + } + + pub fn candidate_bond_more_inner( + collator: T::AccountId, + more: BalanceOf, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let actual_weight = T::WeightInfo::candidate_bond_more(T::MaxCandidates::get()); + + state.bond_more::(collator.clone(), more).map_err(|err| { + DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: err } + })?; + let (is_active, total_counted) = (state.is_active(), state.total_counted); + >::insert(&collator, state); + if is_active { + Self::update_active(collator, total_counted); + } + Ok(Some(actual_weight).into()) + } + + pub fn execute_candidate_bond_less_inner( + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; + let actual_weight = T::WeightInfo::execute_candidate_bond_less(T::MaxCandidates::get()); + + state.execute_bond_less::(candidate.clone()).map_err(|err| { + DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: err } + })?; + >::insert(&candidate, state); + Ok(Some(actual_weight).into()) + } + + pub fn execute_leave_candidates_inner( + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + let state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; + let actual_auto_compound_delegation_count = + >::decode_len(&candidate).unwrap_or_default() as u32; + + // TODO use these to return actual weight used via `execute_leave_candidates_ideal` + let actual_delegation_count = state.delegation_count; + let actual_weight = T::WeightInfo::execute_leave_candidates_ideal( + actual_delegation_count, + actual_auto_compound_delegation_count, + ); + + state.can_leave::().map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; + let return_stake = |bond: Bond>| { + // remove delegation from delegator state + let mut delegator = DelegatorState::::get(&bond.owner).expect( + "Collator state and delegator state are consistent. + Collator state has a record of this delegation. Therefore, + Delegator state also has a record. qed.", + ); + + if let Some(remaining) = delegator.rm_delegation::(&candidate) { + Self::delegation_remove_request_with_state( + &candidate, + &bond.owner, + &mut delegator, + ); + >::remove_auto_compound(&candidate, &bond.owner); + + if remaining.is_zero() { + // we do not remove the scheduled delegation requests from other collators + // since it is assumed that they were removed incrementally before only the + // last delegation was left. + >::remove(&bond.owner); + T::Currency::remove_lock(DELEGATOR_LOCK_ID, &bond.owner); + } else { + >::insert(&bond.owner, delegator); + } + } else { + // TODO: review. we assume here that this delegator has no remaining staked + // balance, so we ensure the lock is cleared + T::Currency::remove_lock(DELEGATOR_LOCK_ID, &bond.owner); + } + }; + // total backing stake is at least the candidate self bond + let mut total_backing = state.bond; + // return all top delegations + let top_delegations = + >::take(&candidate).expect("CandidateInfo existence checked"); + for bond in top_delegations.delegations { + return_stake(bond); + } + total_backing = total_backing.saturating_add(top_delegations.total); + // return all bottom delegations + let bottom_delegations = + >::take(&candidate).expect("CandidateInfo existence checked"); + for bond in bottom_delegations.delegations { + return_stake(bond); + } + total_backing = total_backing.saturating_add(bottom_delegations.total); + // return stake to collator + T::Currency::remove_lock(COLLATOR_LOCK_ID, &candidate); + >::remove(&candidate); + >::remove(&candidate); + >::remove(&candidate); + >::remove(&candidate); + >::remove(&candidate); + let new_total_staked = >::get().saturating_sub(total_backing); + >::put(new_total_staked); + Self::deposit_event(Event::CandidateLeft { + ex_candidate: candidate, + unlocked_amount: total_backing, + new_total_amt_locked: new_total_staked, + }); + Ok(Some(actual_weight).into()) + } + + /// Returns an account's free balance which is not locked in delegation staking + pub fn get_delegator_stakable_free_balance(acc: &T::AccountId) -> BalanceOf { + let mut balance = T::Currency::free_balance(acc); + if let Some(state) = >::get(acc) { + balance = balance.saturating_sub(state.total()); + } + balance + } + + /// Returns an account's free balance which is not locked in collator staking + pub fn get_collator_stakable_free_balance(acc: &T::AccountId) -> BalanceOf { + let mut balance = T::Currency::free_balance(acc); + if let Some(info) = >::get(acc) { + balance = balance.saturating_sub(info.bond); + } + balance + } + + /// Returns a delegations auto-compound value. + pub fn delegation_auto_compound( + candidate: &T::AccountId, + delegator: &T::AccountId, + ) -> Percent { + >::auto_compound(candidate, delegator) + } + + /// Caller must ensure candidate is active before calling + pub(crate) fn update_active(candidate: T::AccountId, total: BalanceOf) { + let mut candidates = >::get(); + candidates.remove(&Bond::from_owner(candidate.clone())); + candidates.try_insert(Bond { owner: candidate, amount: total }).expect( + "the candidate is removed in previous step so the length cannot increase; qed", + ); + >::put(candidates); + } + + /// Compute round issuance based on total staked for the given round + fn compute_issuance(staked: BalanceOf) -> BalanceOf { + let config = >::get(); + let round_issuance = crate::inflation::round_issuance_range::(config.round); + // TODO: consider interpolation instead of bounded range + if staked < config.expect.min { + round_issuance.min + } else if staked > config.expect.max { + round_issuance.max + } else { + round_issuance.ideal + } + } + + /// Remove delegation from candidate state + /// Amount input should be retrieved from delegator and it informs the storage lookups + pub(crate) fn delegator_leaves_candidate( + candidate: T::AccountId, + delegator: T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { + let mut state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; + state.rm_delegation_if_exists::(&candidate, delegator.clone(), amount)?; + let new_total_locked = >::get().saturating_sub(amount); + >::put(new_total_locked); + let new_total = state.total_counted; + >::insert(&candidate, state); + Self::deposit_event(Event::DelegatorLeftCandidate { + delegator, + candidate, + unstaked_amount: amount, + total_candidate_staked: new_total, + }); + Ok(()) + } + + pub(crate) fn prepare_staking_payouts(now: RoundIndex) -> Weight { + // payout is now - delay rounds ago => now - delay > 0 else return early + let delay = T::RewardPaymentDelay::get(); + if now <= delay { + return Weight::zero(); + } + let round_to_payout = now.saturating_sub(delay); + let total_points = >::get(round_to_payout); + if total_points.is_zero() { + return Weight::zero(); + } + let total_staked = >::take(round_to_payout); + let total_issuance = Self::compute_issuance(total_staked); + let mut left_issuance = total_issuance; + // reserve portion of issuance for parachain bond account + let bond_config = >::get(); + let parachain_bond_reserve = bond_config.percent * total_issuance; + if let Ok(amount) = + T::PayoutReward::payout(&bond_config.account, parachain_bond_reserve) + { + // update round issuance iff transfer succeeds + left_issuance = left_issuance.saturating_sub(amount); + Self::deposit_event(Event::ReservedForParachainBond { + account: bond_config.account, + value: amount, + }); + } + + let payout = DelayedPayout { + round_issuance: total_issuance, + total_staking_reward: left_issuance, + collator_commission: >::get(), + }; + + >::insert(round_to_payout, payout); + T::WeightInfo::prepare_staking_payouts() + } + + /// Wrapper around pay_one_collator_reward which handles the following logic: + /// * whether or not a payout needs to be made + /// * cleaning up when payouts are done + /// * returns the weight consumed by pay_one_collator_reward if applicable + fn handle_delayed_payouts(now: RoundIndex) -> Weight { + let delay = T::RewardPaymentDelay::get(); + + // don't underflow uint + if now < delay { + return Weight::from_parts(0u64, 0); + } + + let paid_for_round = now.saturating_sub(delay); + + if let Some(payout_info) = >::get(paid_for_round) { + let result = Self::pay_one_collator_reward(paid_for_round, payout_info); + + // clean up storage items that we no longer need + if matches!(result.0, RewardPayment::Finished) { + >::remove(paid_for_round); + >::remove(paid_for_round); + } + result.1 // weight consumed by pay_one_collator_reward + } else { + Weight::from_parts(0u64, 0) + } + } + + /// Payout a single collator from the given round. + /// + /// Returns an optional tuple of (Collator's AccountId, total paid) + /// or None if there were no more payouts to be made for the round. + pub(crate) fn pay_one_collator_reward( + paid_for_round: RoundIndex, + payout_info: DelayedPayout>, + ) -> (RewardPayment, Weight) { + // 'early_weight' tracks weight used for reads/writes done early in this fn before its + // early-exit codepaths. + let mut early_weight = Weight::zero(); + + // TODO: it would probably be optimal to roll Points into the DelayedPayouts storage + // item so that we do fewer reads each block + let total_points = >::get(paid_for_round); + early_weight = early_weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + + if total_points.is_zero() { + // TODO: this case is obnoxious... it's a value query, so it could mean one of two + // different logic errors: + // 1. we removed it before we should have + // 2. we called pay_one_collator_reward when we were actually done with deferred + // payouts + log::warn!("pay_one_collator_reward called with no > for the round!"); + return (RewardPayment::Finished, early_weight); + } + + let collator_fee = payout_info.collator_commission; + let collator_issuance = collator_fee * payout_info.round_issuance; + if let Some((collator, state)) = + >::iter_prefix(paid_for_round).drain().next() + { + // read and kill AtStake + early_weight = early_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + // Take the awarded points for the collator + let pts = >::take(paid_for_round, &collator); + // read and kill AwardedPts + early_weight = early_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + if pts == 0 { + return (RewardPayment::Skipped, early_weight); + } + + // 'extra_weight' tracks weight returned from fns that we delegate to which can't be + // known ahead of time. + let mut extra_weight = Weight::zero(); + let pct_due = Perbill::from_rational(pts, total_points); + let total_paid = pct_due * payout_info.total_staking_reward; + let mut amt_due = total_paid; + + let num_delegators = state.delegations.len(); + let mut num_paid_delegations = 0u32; + let mut num_auto_compounding = 0u32; + let num_scheduled_requests = + >::decode_len(&collator).unwrap_or_default(); + if state.delegations.is_empty() { + // solo collator with no delegators + extra_weight = extra_weight + .saturating_add(T::PayoutReward::payout_collator_rewards( + paid_for_round, + collator.clone(), + amt_due, + )) + .saturating_add(T::OnCollatorPayout::on_collator_payout( + paid_for_round, + collator.clone(), + amt_due, + )); + } else { + // pay collator first; commission + due_portion + let collator_pct = Perbill::from_rational(state.bond, state.total); + let commission = pct_due * collator_issuance; + amt_due = amt_due.saturating_sub(commission); + let collator_reward = (collator_pct * amt_due).saturating_add(commission); + extra_weight = extra_weight + .saturating_add(T::PayoutReward::payout_collator_rewards( + paid_for_round, + collator.clone(), + collator_reward, + )) + .saturating_add(T::OnCollatorPayout::on_collator_payout( + paid_for_round, + collator.clone(), + collator_reward, + )); + + // pay delegators due portion + for BondWithAutoCompound { owner, amount, auto_compound } in state.delegations { + let percent = Perbill::from_rational(amount, state.total); + let due = percent * amt_due; + if !due.is_zero() { + num_auto_compounding += if auto_compound.is_zero() { 0 } else { 1 }; + num_paid_delegations += 1u32; + Self::mint_and_compound( + due, + auto_compound, + collator.clone(), + owner.clone(), + ); + } + } + } + + extra_weight = + extra_weight.saturating_add(T::WeightInfo::pay_one_collator_reward_best( + num_paid_delegations, + num_auto_compounding, + num_scheduled_requests as u32, + )); + + ( + RewardPayment::Paid, + T::WeightInfo::pay_one_collator_reward(num_delegators as u32) + .saturating_add(extra_weight), + ) + } else { + // Note that we don't clean up storage here; it is cleaned up in + // handle_delayed_payouts() + (RewardPayment::Finished, Weight::from_parts(0u64, 0)) + } + } + + /// Compute the top `TotalSelected` candidates in the CandidatePool and return + /// a vec of their AccountIds (sorted by AccountId). + /// + /// If the returned vec is empty, the previous candidates should be used. + pub fn compute_top_candidates() -> Vec { + let top_n = >::get() as usize; + if top_n == 0 { + return vec![]; + } + + let candidates = >::get().0; + + // If the number of candidates is greater than top_n, select the candidates with higher + // amount. Otherwise, return all the candidates. + if candidates.len() > top_n { + // Partially sort candidates such that element at index `top_n - 1` is sorted, and + // all the elements in the range 0..top_n are the top n elements. + let sorted_candidates = candidates + .try_mutate(|inner| { + inner.select_nth_unstable_by(top_n - 1, |a, b| { + // Order by amount, then owner. The owner is needed to ensure a stable + // order when two accounts have the same amount. + a.amount.cmp(&b.amount).then_with(|| a.owner.cmp(&b.owner)).reverse() + }); + }) + .expect("sort cannot increase item count; qed"); + + let mut collators = + sorted_candidates.into_iter().take(top_n).map(|x| x.owner).collect::>(); + + // Sort collators by AccountId + collators.sort(); + + collators + } else { + // Return all candidates + // The candidates are already sorted by AccountId, so no need to sort again + candidates.into_iter().map(|x| x.owner).collect::>() + } + } + /// Best as in most cumulatively supported in terms of stake + /// Returns [collator_count, delegation_count, total staked] + pub(crate) fn select_top_candidates(now: RoundIndex) -> (Weight, u32, u32, BalanceOf) { + let (mut collator_count, mut delegation_count, mut total) = + (0u32, 0u32, BalanceOf::::zero()); + // choose the top TotalSelected qualified candidates, ordered by stake + let collators = Self::compute_top_candidates(); + if collators.is_empty() { + // SELECTION FAILED TO SELECT >=1 COLLATOR => select collators from previous round + let last_round = now.saturating_sub(1u32); + let mut total_per_candidate: BTreeMap> = BTreeMap::new(); + // set this round AtStake to last round AtStake + for (account, snapshot) in >::iter_prefix(last_round) { + collator_count = collator_count.saturating_add(1u32); + delegation_count = + delegation_count.saturating_add(snapshot.delegations.len() as u32); + total = total.saturating_add(snapshot.total); + total_per_candidate.insert(account.clone(), snapshot.total); + >::insert(now, account, snapshot); + } + // `SelectedCandidates` remains unchanged from last round + // emit CollatorChosen event for tools that use this event + for candidate in >::get() { + let snapshot_total = total_per_candidate + .get(&candidate) + .expect("all selected candidates have snapshots"); + Self::deposit_event(Event::CollatorChosen { + round: now, + collator_account: candidate, + total_exposed_amount: *snapshot_total, + }) + } + let weight = T::WeightInfo::select_top_candidates(0, 0); + return (weight, collator_count, delegation_count, total); + } + + // snapshot exposure for round for weighting reward distribution + for account in collators.iter() { + let state = >::get(account) + .expect("all members of CandidateQ must be candidates"); + + collator_count = collator_count.saturating_add(1u32); + delegation_count = delegation_count.saturating_add(state.delegation_count); + total = total.saturating_add(state.total_counted); + let CountedDelegations { uncounted_stake, rewardable_delegations } = + Self::get_rewardable_delegators(account); + let total_counted = state.total_counted.saturating_sub(uncounted_stake); + + let auto_compounding_delegations = >::get(account) + .into_iter() + .map(|x| (x.delegator, x.value)) + .collect::>(); + let rewardable_delegations = rewardable_delegations + .into_iter() + .map(|d| BondWithAutoCompound { + owner: d.owner.clone(), + amount: d.amount, + auto_compound: auto_compounding_delegations + .get(&d.owner) + .cloned() + .unwrap_or_else(Percent::zero), + }) + .collect(); + + let snapshot = CollatorSnapshot { + bond: state.bond, + delegations: rewardable_delegations, + total: total_counted, + }; + >::insert(now, account, snapshot); + Self::deposit_event(Event::CollatorChosen { + round: now, + collator_account: account.clone(), + total_exposed_amount: state.total_counted, + }); + } + // insert canonical collator set + >::put( + BoundedVec::try_from(collators) + .expect("subset of collators is always less than or equal to max candidates"), + ); + + let avg_delegator_count = delegation_count.checked_div(collator_count).unwrap_or(0); + let weight = T::WeightInfo::select_top_candidates(collator_count, avg_delegator_count); + (weight, collator_count, delegation_count, total) + } + + /// Apply the delegator intent for revoke and decrease in order to build the + /// effective list of delegators with their intended bond amount. + /// + /// This will: + /// - if [DelegationChange::Revoke] is outstanding, set the bond amount to 0. + /// - if [DelegationChange::Decrease] is outstanding, subtract the bond by specified amount. + /// - else, do nothing + /// + /// The intended bond amounts will be used while calculating rewards. + pub(crate) fn get_rewardable_delegators(collator: &T::AccountId) -> CountedDelegations { + let requests = >::get(collator) + .into_iter() + .map(|x| (x.delegator, x.action)) + .collect::>(); + let mut uncounted_stake = BalanceOf::::zero(); + let rewardable_delegations = >::get(collator) + .expect("all members of CandidateQ must be candidates") + .delegations + .into_iter() + .map(|mut bond| { + bond.amount = match requests.get(&bond.owner) { + None => bond.amount, + Some(DelegationAction::Revoke(_)) => { + uncounted_stake = uncounted_stake.saturating_add(bond.amount); + BalanceOf::::zero() + }, + Some(DelegationAction::Decrease(amount)) => { + uncounted_stake = uncounted_stake.saturating_add(*amount); + bond.amount.saturating_sub(*amount) + }, + }; + + bond + }) + .collect(); + CountedDelegations { uncounted_stake, rewardable_delegations } + } + + /// This function exists as a helper to delegator_bond_more & auto_compound functionality. + /// Any changes to this function must align with both user-initiated bond increases and + /// auto-compounding bond increases. + /// Any feature-specific preconditions should be validated before this function is invoked. + /// Any feature-specific events must be emitted after this function is invoked. + pub fn delegation_bond_more_without_event( + delegator: T::AccountId, + candidate: T::AccountId, + more: BalanceOf, + ) -> Result< + (bool, Weight), + DispatchErrorWithPostInfo, + > { + let mut state = >::get(&delegator).ok_or(Error::::DelegatorDNE)?; + ensure!( + !Self::delegation_request_revoke_exists(&candidate, &delegator), + Error::::PendingDelegationRevoke + ); + + let actual_weight = T::WeightInfo::delegator_bond_more( + >::decode_len(&candidate).unwrap_or_default() as u32, + ); + let in_top = + state.increase_delegation::(candidate.clone(), more).map_err(|err| { + DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: err } + })?; + + Ok((in_top, actual_weight)) + } + + /// Mint a specified reward amount to the beneficiary account. Emits the [Rewarded] event. + pub fn mint(amt: BalanceOf, to: T::AccountId) { + if let Ok(amount_transferred) = T::PayoutReward::payout(&to, amt) { + Self::deposit_event(Event::Rewarded { + account: to.clone(), + rewards: amount_transferred, + }); + } + } + + /// Mint and compound delegation rewards. The function mints the amount towards the + /// delegator and tries to compound a specified percent of it back towards the delegation. + /// If a scheduled delegation revoke exists, then the amount is only minted, and nothing is + /// compounded. Emits the [Compounded] event. + pub fn mint_and_compound( + amt: BalanceOf, + compound_percent: Percent, + candidate: T::AccountId, + delegator: T::AccountId, + ) { + if let Ok(amount_transferred) = T::PayoutReward::payout(&delegator, amt) { + Self::deposit_event(Event::Rewarded { + account: delegator.clone(), + rewards: amount_transferred, + }); + + let compound_amount = compound_percent.mul_ceil(amount_transferred); + if compound_amount.is_zero() { + return; + } + + if let Err(err) = Self::delegation_bond_more_without_event( + delegator.clone(), + candidate.clone(), + compound_amount, + ) { + log::debug!( + "skipped compounding staking reward towards candidate '{:?}' for delegator '{:?}': {:?}", + candidate, + delegator, + err + ); + return; + }; + + Pallet::::deposit_event(Event::Compounded { + delegator, + candidate, + amount: compound_amount, + }); + }; + } + } + + /// Add reward points to block authors: + /// * 20 points to the block producer for producing a block in the chain + impl Pallet { + fn award_points_to_block_author() { + let author = T::BlockAuthor::get(); + let now = >::get().current; + let score_plus_20 = >::get(now, &author).saturating_add(20); + >::insert(now, author, score_plus_20); + >::mutate(now, |x| *x = x.saturating_add(20)); + } + } + + impl Get> for Pallet { + fn get() -> Vec { + Self::selected_candidates().into_inner() + } + } +} diff --git a/pallets/parachain-staking-old/src/migrations.rs b/pallets/parachain-staking-old/src/migrations.rs new file mode 100644 index 000000000..19d43bcf5 --- /dev/null +++ b/pallets/parachain-staking-old/src/migrations.rs @@ -0,0 +1,17 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! # Migrations diff --git a/pallets/parachain-staking-old/src/mock.rs b/pallets/parachain-staking-old/src/mock.rs new file mode 100644 index 000000000..a6b1b0af6 --- /dev/null +++ b/pallets/parachain-staking-old/src/mock.rs @@ -0,0 +1,785 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! Test utilities +use crate::{ + self as pallet_parachain_staking, pallet, rewards, AwardedPts, Config, + Event as ParachainStakingEvent, InflationInfo, Points, Range, COLLATOR_LOCK_ID, + DELEGATOR_LOCK_ID, +}; +use block_author::BlockAuthor as BlockAuthorMap; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{Get, LockIdentifier, OnFinalize, OnInitialize}, + weights::{constants::RocksDbWeight, Weight}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_consensus_slots::Slot; +use sp_core::ConstU32; +use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill, Percent}; + +pub use test_utils::*; + +pub type AccountId = u64; +pub type Balance = u128; +pub type BlockNumber = BlockNumberFor; + +type Block = frame_system::mocking::MockBlockU32; + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + ParachainStaking: pallet_parachain_staking, + BlockAuthor: block_author, + } +); + +parameter_types! { + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type BlockHashCount = ConstU32<250>; + type AccountData = pallet_balances::AccountData; + type DbWeight = RocksDbWeight; +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 0; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Test { + type Balance = Balance; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type RuntimeHoldReason = (); + type DustRemoval = (); +} + +impl block_author::Config for Test {} +const GENESIS_BLOCKS_PER_ROUND: BlockNumber = 5; +const GENESIS_COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20); +const GENESIS_PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30); +const GENESIS_NUM_SELECTED_CANDIDATES: u32 = 5; + +parameter_types! { + pub const MinBlocksPerRound: u32 = 3; + pub const MaxOfflineRounds: u32 = 1; + pub const LeaveCandidatesDelay: u32 = 2; + pub const CandidateBondLessDelay: u32 = 2; + pub const LeaveDelegatorsDelay: u32 = 2; + pub const RevokeDelegationDelay: u32 = 2; + pub const DelegationBondLessDelay: u32 = 2; + pub const RewardPaymentDelay: u32 = 2; + pub const MinSelectedCandidates: u32 = GENESIS_NUM_SELECTED_CANDIDATES; + pub const MaxTopDelegationsPerCandidate: u32 = 4; + pub const MaxBottomDelegationsPerCandidate: u32 = 4; + pub const MaxDelegationsPerDelegator: u32 = 4; + pub const MinCandidateStk: u128 = 10; + pub const MinDelegation: u128 = 3; + pub const MaxCandidates: u32 = 200; +} + +pub struct StakingRoundSlotProvider; +impl Get for StakingRoundSlotProvider { + fn get() -> Slot { + let block_number: u64 = System::block_number().into(); + Slot::from(block_number) + } +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MonetaryGovernanceOrigin = frame_system::EnsureRoot; + type MinBlocksPerRound = MinBlocksPerRound; + type MaxOfflineRounds = MaxOfflineRounds; + type LeaveCandidatesDelay = LeaveCandidatesDelay; + type CandidateBondLessDelay = CandidateBondLessDelay; + type LeaveDelegatorsDelay = LeaveDelegatorsDelay; + type RevokeDelegationDelay = RevokeDelegationDelay; + type DelegationBondLessDelay = DelegationBondLessDelay; + type RewardPaymentDelay = RewardPaymentDelay; + type MinSelectedCandidates = MinSelectedCandidates; + type MaxTopDelegationsPerCandidate = MaxTopDelegationsPerCandidate; + type MaxBottomDelegationsPerCandidate = MaxBottomDelegationsPerCandidate; + type MaxDelegationsPerDelegator = MaxDelegationsPerDelegator; + type MinCandidateStk = MinCandidateStk; + type MinDelegation = MinDelegation; + type BlockAuthor = BlockAuthor; + type OnCollatorPayout = (); + type PayoutReward = rewards::TransferFromRewardsAccount; + type OnInactiveCollator = (); + type OnNewRound = (); + type SlotProvider = StakingRoundSlotProvider; + type WeightInfo = (); + type MaxCandidates = MaxCandidates; + type SlotsPerYear = frame_support::traits::ConstU32<{ 31_557_600 / 6 }>; +} + +pub(crate) struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, + // [collator, amount] + collators: Vec<(AccountId, Balance)>, + // [delegator, collator, delegation_amount, auto_compound_percent] + delegations: Vec<(AccountId, AccountId, Balance, Percent)>, + // inflation config + inflation: InflationInfo, + // rewards account balance + rewards_account: Option<(AccountId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: vec![], + delegations: vec![], + collators: vec![], + inflation: InflationInfo { + expect: Range { min: 700, ideal: 700, max: 700 }, + // not used + annual: Range { + min: Perbill::from_percent(50), + ideal: Perbill::from_percent(50), + max: Perbill::from_percent(50), + }, + // unrealistically high parameterization, only for testing + round: Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }, + }, + rewards_account: None, + } + } +} + +impl ExtBuilder { + pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub(crate) fn with_candidates(mut self, collators: Vec<(AccountId, Balance)>) -> Self { + self.collators = collators; + self + } + + pub(crate) fn with_delegations( + mut self, + delegations: Vec<(AccountId, AccountId, Balance)>, + ) -> Self { + self.delegations = + delegations.into_iter().map(|d| (d.0, d.1, d.2, Percent::zero())).collect(); + self + } + + pub(crate) fn with_auto_compounding_delegations( + mut self, + delegations: Vec<(AccountId, AccountId, Balance, Percent)>, + ) -> Self { + self.delegations = delegations; + self + } + + pub(crate) fn with_rewards_account(mut self, account: AccountId, balance: Balance) -> Self { + self.rewards_account = Some((account, balance)); + self + } + + #[allow(dead_code)] + pub(crate) fn with_inflation(mut self, inflation: InflationInfo) -> Self { + self.inflation = inflation; + self + } + + pub(crate) fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .expect("Frame system builds valid default genesis config"); + + let mut rewards_account = None; + let mut balances = self.balances.clone(); + if let Some((account, balance)) = self.rewards_account { + balances.push((account, balance)); + rewards_account = Some(account); + } + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + pallet_parachain_staking::GenesisConfig:: { + candidates: self.collators, + delegations: self.delegations, + inflation_config: self.inflation, + collator_commission: GENESIS_COLLATOR_COMMISSION, + parachain_bond_reserve_percent: GENESIS_PARACHAIN_BOND_RESERVE_PERCENT, + blocks_per_round: GENESIS_BLOCKS_PER_ROUND, + num_selected_candidates: GENESIS_NUM_SELECTED_CANDIDATES, + rewards_account, + } + .assimilate_storage(&mut t) + .expect("Parachain Staking's storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +/// Rolls to the desired block. Returns the number of blocks played. +pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber { + let mut num_blocks = 0; + let mut block = System::block_number(); + while block < n { + roll_one_block!(true); + block = System::block_number(); + num_blocks += 1; + } + num_blocks +} + +/// Rolls desired number of blocks. Returns the final block. +pub(crate) fn roll_blocks(num_blocks: u32) -> BlockNumber { + for _ in 0..num_blocks { + roll_one_block!(true); + } + System::block_number() +} + +/// Rolls block-by-block to the beginning of the specified round. +/// This will complete the block in which the round change occurs. +/// Returns the number of blocks played. +pub(crate) fn roll_to_round_begin(round: BlockNumber) -> BlockNumber { + let block = (round - 1) * GENESIS_BLOCKS_PER_ROUND; + roll_to(block) +} + +/// Rolls block-by-block to the end of the specified round. +/// The block following will be the one in which the specified round change occurs. +pub(crate) fn roll_to_round_end(round: BlockNumber) -> BlockNumber { + let block = round * GENESIS_BLOCKS_PER_ROUND - 1; + roll_to(block) +} + +pub(crate) fn events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map( + |e| { + if let RuntimeEvent::ParachainStaking(inner) = e { + Some(inner) + } else { + None + } + }, + ) + .collect::>() +} + +// Same storage changes as ParachainStaking::on_finalize +pub(crate) fn set_author(round: BlockNumber, acc: u64, pts: u32) { + >::mutate(round, |p| *p += pts); + >::mutate(round, acc, |p| *p += pts); +} + +// Allows to change the block author (default is always 0) +pub(crate) fn set_block_author(acc: u64) { + >::set(acc); +} + +/// fn to query the lock amount +pub(crate) fn query_lock_amount(account_id: u64, id: LockIdentifier) -> Option { + for lock in Balances::locks(account_id) { + if lock.id == id { + return Some(lock.amount); + } + } + None +} + +#[test] +fn geneses() { + ExtBuilder::default() + .with_balances(vec![ + (1, 1000), + (2, 300), + (3, 100), + (4, 100), + (5, 100), + (6, 100), + (7, 100), + (8, 9), + (9, 4), + ]) + .with_candidates(vec![(1, 500), (2, 200)]) + .with_delegations(vec![(3, 1, 100), (4, 1, 100), (5, 2, 100), (6, 2, 100)]) + .build() + .execute_with(|| { + assert!(System::events().is_empty()); + // collators + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 500); + assert_eq!(query_lock_amount(1, COLLATOR_LOCK_ID), Some(500)); + assert!(ParachainStaking::is_candidate(&1)); + assert_eq!(query_lock_amount(2, COLLATOR_LOCK_ID), Some(200)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&2), 100); + assert!(ParachainStaking::is_candidate(&2)); + // delegators + for x in 3..7 { + assert!(ParachainStaking::is_delegator(&x)); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&x), 0); + assert_eq!(query_lock_amount(x, DELEGATOR_LOCK_ID), Some(100)); + } + // uninvolved + for x in 7..10 { + assert!(!ParachainStaking::is_delegator(&x)); + } + // no delegator staking locks + assert_eq!(query_lock_amount(7, DELEGATOR_LOCK_ID), None); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&7), 100); + assert_eq!(query_lock_amount(8, DELEGATOR_LOCK_ID), None); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&8), 9); + assert_eq!(query_lock_amount(9, DELEGATOR_LOCK_ID), None); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&9), 4); + // no collator staking locks + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&7), 100); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&8), 9); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&9), 4); + }); + 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_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) + .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .build() + .execute_with(|| { + assert!(System::events().is_empty()); + // collators + for x in 1..5 { + assert!(ParachainStaking::is_candidate(&x)); + assert_eq!(query_lock_amount(x, COLLATOR_LOCK_ID), Some(20)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&x), 80); + } + assert!(ParachainStaking::is_candidate(&5)); + assert_eq!(query_lock_amount(5, COLLATOR_LOCK_ID), Some(10)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&5), 90); + // delegators + for x in 6..11 { + assert!(ParachainStaking::is_delegator(&x)); + assert_eq!(query_lock_amount(x, DELEGATOR_LOCK_ID), Some(10)); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&x), 90); + } + }); +} + +#[allow(unused_imports)] +#[frame_support::pallet] +pub(crate) mod block_author { + use super::*; + use frame_support::{pallet_prelude::*, traits::Get}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn block_author)] + pub(super) type BlockAuthor = StorageValue<_, AccountId, ValueQuery>; + + impl Get for Pallet { + fn get() -> AccountId { + >::get() + } + } +} + +#[test] +fn roll_to_round_begin_works() { + ExtBuilder::default().build().execute_with(|| { + // these tests assume blocks-per-round of 5, as established by GENESIS_BLOCKS_PER_ROUND + assert_eq!(System::block_number(), 1); // we start on block 1 + + let num_blocks = roll_to_round_begin(1); + assert_eq!(System::block_number(), 1); // no-op, we're already on this round + assert_eq!(num_blocks, 0); + + let num_blocks = roll_to_round_begin(2); + assert_eq!(System::block_number(), 5); + assert_eq!(num_blocks, 4); + + let num_blocks = roll_to_round_begin(3); + assert_eq!(System::block_number(), 10); + assert_eq!(num_blocks, 5); + }); +} + +#[test] +fn roll_to_round_end_works() { + ExtBuilder::default().build().execute_with(|| { + // these tests assume blocks-per-round of 5, as established by GENESIS_BLOCKS_PER_ROUND + assert_eq!(System::block_number(), 1); // we start on block 1 + + let num_blocks = roll_to_round_end(1); + assert_eq!(System::block_number(), 4); + assert_eq!(num_blocks, 3); + + let num_blocks = roll_to_round_end(2); + assert_eq!(System::block_number(), 9); + assert_eq!(num_blocks, 5); + + let num_blocks = roll_to_round_end(3); + assert_eq!(System::block_number(), 14); + assert_eq!(num_blocks, 5); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_fails_if_event_missing() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq!( + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 10, + }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_fails_if_event_extra() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq!( + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 10, + }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 200 }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_fails_if_event_wrong_order() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq!( + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 10, + }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_fails_if_event_wrong_value() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq!( + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 10, + }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 50 }, + ); + }); +} + +#[test] +fn test_assert_events_eq_passes_if_all_events_present_single() { + ExtBuilder::default().build().execute_with(|| { + System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + + assert_events_eq!(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + }); +} + +#[test] +fn test_assert_events_eq_passes_if_all_events_present_multiple() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq!( + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 10, + }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_emitted_fails_if_event_missing() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_emitted!(ParachainStakingEvent::DelegatorExitScheduled { + round: 2, + delegator: 3, + scheduled_exit: 4, + }); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_emitted_fails_if_event_wrong_value() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_emitted!(ParachainStakingEvent::Rewarded { account: 1, rewards: 50 }); + }); +} + +#[test] +fn test_assert_events_emitted_passes_if_all_events_present_single() { + ExtBuilder::default().build().execute_with(|| { + System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + + assert_events_emitted!(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + }); +} + +#[test] +fn test_assert_events_emitted_passes_if_all_events_present_multiple() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_emitted!( + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_match_fails_if_event_missing() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq_match!( + ParachainStakingEvent::CollatorChosen { .. }, + ParachainStakingEvent::NewRound { .. }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_match_fails_if_event_extra() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq_match!( + ParachainStakingEvent::CollatorChosen { .. }, + ParachainStakingEvent::NewRound { .. }, + ParachainStakingEvent::Rewarded { .. }, + ParachainStakingEvent::Rewarded { .. }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_match_fails_if_event_wrong_order() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq_match!( + ParachainStakingEvent::Rewarded { .. }, + ParachainStakingEvent::CollatorChosen { .. }, + ParachainStakingEvent::NewRound { .. }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_eq_match_fails_if_event_wrong_value() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq_match!( + ParachainStakingEvent::CollatorChosen { .. }, + ParachainStakingEvent::NewRound { .. }, + ParachainStakingEvent::Rewarded { rewards: 50, .. }, + ); + }); +} + +#[test] +fn test_assert_events_eq_match_passes_if_all_events_present_single() { + ExtBuilder::default().build().execute_with(|| { + System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + + assert_events_eq_match!(ParachainStakingEvent::Rewarded { account: 1, .. }); + }); +} + +#[test] +fn test_assert_events_eq_match_passes_if_all_events_present_multiple() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_eq_match!( + ParachainStakingEvent::CollatorChosen { round: 2, collator_account: 1, .. }, + ParachainStakingEvent::NewRound { starting_block: 10, .. }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_emitted_match_fails_if_event_missing() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_emitted_match!(ParachainStakingEvent::DelegatorExitScheduled { + round: 2, + .. + }); + }); +} + +#[test] +#[should_panic] +fn test_assert_events_emitted_match_fails_if_event_wrong_value() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_emitted_match!(ParachainStakingEvent::Rewarded { rewards: 50, .. }); + }); +} + +#[test] +fn test_assert_events_emitted_match_passes_if_all_events_present_single() { + ExtBuilder::default().build().execute_with(|| { + System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + + assert_events_emitted_match!(ParachainStakingEvent::Rewarded { rewards: 100, .. }); + }); +} + +#[test] +fn test_assert_events_emitted_match_passes_if_all_events_present_multiple() { + ExtBuilder::default().build().execute_with(|| { + inject_test_events(); + + assert_events_emitted_match!( + ParachainStakingEvent::CollatorChosen { total_exposed_amount: 10, .. }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ); + }); +} + +fn inject_test_events() { + [ + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 10, + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 10, + }, + ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ] + .into_iter() + .for_each(System::deposit_event); +} diff --git a/pallets/parachain-staking-old/src/rewards/mint_rewards.rs b/pallets/parachain-staking-old/src/rewards/mint_rewards.rs new file mode 100644 index 000000000..7f1c448ea --- /dev/null +++ b/pallets/parachain-staking-old/src/rewards/mint_rewards.rs @@ -0,0 +1,113 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +use crate::{traits::PayoutReward, BalanceOf, Config, Event, Pallet, RoundIndex, WeightInfo}; +use frame_support::{ + pallet_prelude::Weight, + traits::{tokens::currency::Currency, Imbalance}, +}; +use sp_runtime::DispatchError; + +pub struct MintingRewards; +impl PayoutReward for MintingRewards { + fn payout_collator_rewards( + for_round: RoundIndex, + collator_id: Runtime::AccountId, + amount: crate::BalanceOf, + ) -> Weight { + crate::Pallet::::mint_collator_reward(for_round, collator_id, amount) + } + + fn payout( + delegator_id: &Runtime::AccountId, + amount: crate::BalanceOf, + ) -> Result, DispatchError> { + Runtime::Currency::deposit_into_existing(delegator_id, amount) + .map(|imbalance| imbalance.peek()) + } +} + +impl Pallet { + /// Mint a specified reward amount to the collator's account. Emits the [Rewarded] event. + pub(crate) fn mint_collator_reward( + _round_idx: RoundIndex, + collator_id: T::AccountId, + amt: BalanceOf, + ) -> Weight { + match T::Currency::deposit_into_existing(&collator_id, amt) { + Ok(amount_transferred) => { + Self::deposit_event(Event::Rewarded { + account: collator_id.clone(), + rewards: amount_transferred.peek(), + }); + }, + Err(e) => { + log::error!( + "Failed to deposit reward of {:?} to collator {:?}: {:?}", + amt, + collator_id, + e + ); + }, + } + T::WeightInfo::mint_collator_reward() + } +} + +// tests +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::*; + + #[test] + fn test_mint_reward_for_nonexistent_collator_does_not_emit_event() { + ExtBuilder::default().build().execute_with(|| { + let collator = 1; + let amount = 100; + + Pallet::::mint_collator_reward(0, collator, amount); + + assert_eq!(System::events().len(), 0); + }) + } + + #[test] + fn test_mint_zero_rewards_for_collator_emits_rewarded_event() { + ExtBuilder::default().build().execute_with(|| { + let collator = 1; + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + Pallet::::mint_collator_reward(0, collator, 0); + + assert_events_eq_match!(Event::Rewarded { account: 1, rewards: 0 },); + }) + } + + #[test] + fn test_mint_reward_for_existing_collator_emits_rewarded_event() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let collator = 1; + + assert_eq!(System::events().len(), 0); + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + + Pallet::::mint_collator_reward(0, collator, 100); + + assert_events_eq_match!(Event::Rewarded { account: 1, rewards: 100 },); + }) + } +} diff --git a/pallets/parachain-staking-old/src/rewards/mod.rs b/pallets/parachain-staking-old/src/rewards/mod.rs new file mode 100644 index 000000000..25f5b1e6f --- /dev/null +++ b/pallets/parachain-staking-old/src/rewards/mod.rs @@ -0,0 +1,153 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +mod mint_rewards; +mod transfer_from_rewards_account; + +pub use mint_rewards::MintingRewards; +pub use transfer_from_rewards_account::TransferFromRewardsAccount; + +// These tests aim to verify the PayoutReward trait's behavior through its concrete implementations, +// ensuring they function as anticipated. +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, Error, Event, PayoutReward, RoundIndex}; + use frame_support::{ + assert_err, assert_ok, pallet_prelude::Weight, traits::tokens::currency::Currency, + }; + use sp_runtime::DispatchError; + + fn paying_collator_rewards>( + round_index: RoundIndex, + collator: AccountId, + amount: Balance, + ) -> Weight { + T::payout_collator_rewards(round_index, collator, amount) + } + + fn paying>( + destination: AccountId, + amount: Balance, + ) -> Result { + T::payout(&destination, amount) + } + + #[test] + fn test_payout_unexistent_collator_does_nothing() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let collator = 10; + let amount = 8; + let round_index = 0; + + paying_collator_rewards::(round_index, collator, amount); + paying_collator_rewards::(round_index, collator, amount); + + assert_eq!(pallet_balances::Pallet::::free_balance(collator), 0); + + assert_no_events!(); + }); + } + + #[test] + fn test_payout_collator_non_zero_rewards() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let collator = 10; + let amount = 8; + let round_index = 0; + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + + paying_collator_rewards::(round_index, collator, amount); + paying_collator_rewards::(round_index, collator, amount); + + assert_eq!(pallet_balances::Pallet::::free_balance(collator), 17); + + assert_events_eq_match!( + Event::Rewarded { account: 10, rewards: 8 }, + Event::Rewarded { account: 10, rewards: 8 }, + ); + }); + } + + #[test] + fn test_payout_collator_zero_rewards() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let collator = 10; + let amount = 0; + let round_index = 0; + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + + paying_collator_rewards::(round_index, collator, amount); + paying_collator_rewards::(round_index, collator, amount); + + assert_eq!(pallet_balances::Pallet::::free_balance(collator), 1); + + assert_events_eq_match!( + Event::Rewarded { account: 10, rewards: 0 }, + Event::Rewarded { account: 10, rewards: 0 }, + ); + }); + } + + #[test] + fn test_payout_nonexistent_account_fails() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let delegator = 9; + let amount = 100; + + assert_err!( + paying::(delegator, amount), + pallet_balances::Error::::DeadAccount + ); + assert_err!( + paying::(delegator, amount), + Error::::DeadAccount + ); + }); + } + + #[test] + fn test_payout_zero_amount() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let delegator = 9; + let amount = 0; + + let _ = pallet_balances::Pallet::::deposit_creating(&delegator, 1); + + assert_ok!(paying::(delegator, amount), 0); + assert_ok!(paying::(delegator, amount), 0); + + assert_eq!(pallet_balances::Pallet::::free_balance(delegator), 1); + }); + } + + #[test] + fn test_payout_non_zero_amount() { + ExtBuilder::default().with_rewards_account(999, 100).build().execute_with(|| { + let delegator = 9; + let amount = 100; + + let _ = pallet_balances::Pallet::::deposit_creating(&delegator, 1); + + assert_ok!(paying::(delegator, amount), 100); + assert_ok!(paying::(delegator, amount), 100); + + assert_eq!(pallet_balances::Pallet::::free_balance(delegator), 201); + }); + } +} diff --git a/pallets/parachain-staking-old/src/rewards/transfer_from_rewards_account.rs b/pallets/parachain-staking-old/src/rewards/transfer_from_rewards_account.rs new file mode 100644 index 000000000..88aff6c68 --- /dev/null +++ b/pallets/parachain-staking-old/src/rewards/transfer_from_rewards_account.rs @@ -0,0 +1,227 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +use crate::{ + traits::PayoutReward, BalanceOf, Config, Error, Event, Pallet, RewardsAccount, RoundIndex, + WeightInfo, +}; +use frame_support::{ + ensure, + pallet_prelude::Weight, + traits::tokens::{currency::Currency, ExistenceRequirement}, +}; +use sp_runtime::{traits::Zero, ArithmeticError, DispatchError}; + +pub struct TransferFromRewardsAccount; +impl PayoutReward for TransferFromRewardsAccount { + fn payout_collator_rewards( + for_round: crate::RoundIndex, + collator_id: Runtime::AccountId, + amount: crate::BalanceOf, + ) -> Weight { + crate::Pallet::::send_collator_rewards(for_round, collator_id, amount) + } + + fn payout( + delegator_id: &Runtime::AccountId, + amount: crate::BalanceOf, + ) -> Result, DispatchError> { + // Early return if amount is zero, + if amount.is_zero() { + return Ok(Zero::zero()); + } + + // Early return if RewardsAccount is not set. + let rewards_account = match RewardsAccount::::get() { + Some(account) => account, + None => return Ok(Zero::zero()), + }; + + // Ensure the destination account exists with a clearer error message. + ensure!( + frame_system::Account::::contains_key(delegator_id), + Error::::DeadAccount + ); + + // Directly handle the result of the transfer, making use of match + // for clearer error handling. + match Runtime::Currency::transfer( + &rewards_account, + delegator_id, + amount, + ExistenceRequirement::KeepAlive, + ) { + Ok(_) => Ok(amount), + Err(DispatchError::Arithmetic(ArithmeticError::Underflow)) => Ok(Zero::zero()), + Err(e) => Err(e), + } + } +} + +impl Pallet { + pub fn send_collator_rewards( + _round_idx: RoundIndex, + collator_id: T::AccountId, + amount: BalanceOf, + ) -> Weight { + // Check if the collator's account exists; return early if not. + if !frame_system::Account::::contains_key(&collator_id) { + return T::WeightInfo::send_collator_rewards(); + } + + // Attempt to get the RewardsAccount and return early if not set. + let rewards_account = match RewardsAccount::::get() { + Some(account) => account, + None => { + return T::WeightInfo::send_collator_rewards(); + }, + }; + + // Proceed with the transfer and handle the result. + let transfer_result = T::Currency::transfer( + &rewards_account, + &collator_id, + amount, + ExistenceRequirement::KeepAlive, + ); + + if let Err(e) = transfer_result { + log::error!("💥 Failed to send rewards to collator: {:?}, amount: {:?}", e, amount); + } else { + Self::deposit_event(Event::Rewarded { account: collator_id, rewards: amount }); + } + + T::WeightInfo::send_collator_rewards() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::*; + use frame_support::{assert_ok, assert_storage_noop}; + + #[test] + fn test_payout_collator_without_rewards_account() { + ExtBuilder::default().build().execute_with(|| { + let collator = 10; + let amount = 8; + let round_index = 0; + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + + >::payout_collator_rewards( + round_index, + collator, + amount, + ); + + assert_eq!(pallet_balances::Pallet::::free_balance(collator), 1); + + assert_no_events!(); + }); + } + + #[test] + fn test_payout_collator_with_not_enough_funds_in_rewards_account() { + ExtBuilder::default().with_rewards_account(999, 7).build().execute_with(|| { + let collator = 10; + let amount = 8; + let round_index = 0; + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + + >::payout_collator_rewards( + round_index, + collator, + amount, + ); + + assert_eq!(pallet_balances::Pallet::::free_balance(collator), 1); + + assert_no_events!(); + }); + } + + #[test] + fn test_payout_with_no_rewards_account_should_do_nothing() { + ExtBuilder::default().build().execute_with(|| { + let delegator = 0; + let amount = 100; + + let _ = pallet_balances::Pallet::::deposit_creating(&delegator, 1); + + assert_storage_noop!(>::payout( + &delegator, amount + ) + .unwrap()); + }); + } + + #[test] + fn test_payout_with_insufficient_rewards_account_funds_succeeds() { + ExtBuilder::default().with_rewards_account(999, 10).build().execute_with(|| { + let delegator = 0; + let amount = 100; + + let _ = pallet_balances::Pallet::::deposit_creating(&delegator, 1); + + assert_ok!( + >::payout(&delegator, amount), + 0 + ); + }); + } + + #[test] + fn test_send_reward_to_nonexistent_collator_does_not_emit_event() { + ExtBuilder::default().with_rewards_account(2, 100).build().execute_with(|| { + let collator = 1; + + Pallet::::send_collator_rewards(0, collator, 100); + + assert_no_events!() + }) + } + + #[test] + fn test_send_zero_rewards_to_collator_emits_rewarded_event() { + ExtBuilder::default().with_rewards_account(2, 1).build().execute_with(|| { + let collator = 1; + let amount = 0; + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + Pallet::::send_collator_rewards(0, collator, amount); + + assert_events_eq_match!(Event::Rewarded { account: 1, rewards: 0 },); + }) + } + + #[test] + fn test_send_rewards_to_existing_collator_emits_rewarded_event() { + ExtBuilder::default().with_rewards_account(2, 100).build().execute_with(|| { + let collator = 1; + + assert_eq!(System::events().len(), 0); + + let _ = pallet_balances::Pallet::::deposit_creating(&collator, 1); + + Pallet::::send_collator_rewards(0, collator, 100); + + assert_events_eq_match!(Event::Rewarded { account: 1, rewards: 100 },); + }) + } +} diff --git a/pallets/parachain-staking-old/src/set.rs b/pallets/parachain-staking-old/src/set.rs new file mode 100644 index 000000000..9cae12f71 --- /dev/null +++ b/pallets/parachain-staking-old/src/set.rs @@ -0,0 +1,166 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +/* TODO: use orml_utilities::OrderedSet without leaking substrate v2.0 dependencies */ + +use frame_support::traits::Get; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_runtime::{BoundedVec, RuntimeDebug}; +use sp_std::prelude::*; + +/// An ordered set backed by `Vec` +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Default, Clone, TypeInfo)] +pub struct OrderedSet(pub Vec); + +impl OrderedSet { + /// Create a new empty set + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Create a set from a `Vec`. + /// `v` will be sorted and dedup first. + pub fn from(mut v: Vec) -> Self { + v.sort(); + v.dedup(); + Self::from_sorted_set(v) + } + + /// Create a set from a `Vec`. + /// Assume `v` is sorted and contain unique elements. + pub fn from_sorted_set(v: Vec) -> Self { + Self(v) + } + + /// Insert an element. + /// Return true if insertion happened. + pub fn insert(&mut self, value: T) -> bool { + match self.0.binary_search(&value) { + Ok(_) => false, + Err(loc) => { + self.0.insert(loc, value); + true + }, + } + } + + /// Remove an element. + /// Return true if removal happened. + pub fn remove(&mut self, value: &T) -> bool { + match self.0.binary_search(value) { + Ok(loc) => { + self.0.remove(loc); + true + }, + Err(_) => false, + } + } + + /// Return if the set contains `value` + pub fn contains(&self, value: &T) -> bool { + self.0.binary_search(value).is_ok() + } + + /// Clear the set + pub fn clear(&mut self) { + self.0.clear(); + } +} + +impl From> for OrderedSet { + fn from(v: Vec) -> Self { + Self::from(v) + } +} + +/// An ordered set backed by `BoundedVec` +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Clone, TypeInfo)] +#[scale_info(skip_type_params(S))] +pub struct BoundedOrderedSet>(pub BoundedVec); + +impl> sp_std::default::Default for BoundedOrderedSet { + fn default() -> Self { + BoundedOrderedSet(BoundedVec::default()) + } +} + +impl> BoundedOrderedSet { + /// Create a new empty set + pub fn new() -> Self { + Self(BoundedVec::default()) + } + + /// Create a set from a `Vec`. + /// `v` will be sorted and dedup first. + pub fn from(mut v: BoundedVec) -> Self { + v.sort(); + let v = v.try_mutate(|inner| inner.dedup()).expect( + "input is a valid BoundedVec and deduping can only reduce the number of entires; qed", + ); + Self::from_sorted_set(v) + } + + /// Create a set from a `Vec`. + /// Assume `v` is sorted and contain unique elements. + pub fn from_sorted_set(v: BoundedVec) -> Self { + Self(v) + } + + /// Insert an element. + /// Return true if insertion happened. + pub fn try_insert(&mut self, value: T) -> Result { + match self.0.binary_search(&value) { + Ok(_) => Ok(false), + Err(loc) => self.0.try_insert(loc, value).map(|_| true).map_err(|_| ()), + } + } + + /// Remove an element. + /// Return true if removal happened. + pub fn remove(&mut self, value: &T) -> bool { + match self.0.binary_search(value) { + Ok(loc) => { + self.0.remove(loc); + true + }, + Err(_) => false, + } + } + + /// Return if the set contains `value` + pub fn contains(&self, value: &T) -> bool { + self.0.binary_search(value).is_ok() + } + + /// Clear the set + pub fn clear(mut self) { + let v = self.0.try_mutate(|inner| inner.clear()).expect( + "input is a valid BoundedVec and clearing can only reduce the number of entires; qed", + ); + self.0 = v; + } +} + +impl> From> for BoundedOrderedSet { + fn from(v: BoundedVec) -> Self { + Self::from(v) + } +} diff --git a/pallets/parachain-staking-old/src/tests.rs b/pallets/parachain-staking-old/src/tests.rs new file mode 100644 index 000000000..194cc077b --- /dev/null +++ b/pallets/parachain-staking-old/src/tests.rs @@ -0,0 +1,6835 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! # Staking Pallet Unit Tests +//! The unit tests are organized by the call they test. The order matches the order +//! of the calls in the `lib.rs`. +//! 1. Root +//! 2. Monetary Governance +//! 3. Public (Collator, Nominator) +//! 4. Miscellaneous Property-Based Tests + +use crate::{ + auto_compound::{AutoCompoundConfig, AutoCompoundDelegations}, + delegation_requests::{CancelledScheduledRequest, DelegationAction, ScheduledRequest}, + mock::{ + self, assert_events_emitted, assert_events_emitted_match, assert_events_eq, + assert_no_events, roll_blocks, roll_to, roll_to_round_begin, roll_to_round_end, set_author, + set_block_author, Balances, BlockNumber, ExtBuilder, ParachainStaking, RuntimeOrigin, Test, + }, + AtStake, Bond, CollatorStatus, DelegationScheduledRequests, DelegatorAdded, + EnableMarkingOffline, Error, Event, InflationInfo, Range, DELEGATOR_LOCK_ID, +}; +use frame_support::{assert_err, assert_noop, assert_ok, pallet_prelude::*, BoundedVec}; +use sp_runtime::{traits::Zero, DispatchError, ModuleError, Perbill, Percent}; + +// ~~ ROOT ~~ + +#[test] +fn invalid_root_origin_fails() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_total_selected(RuntimeOrigin::signed(45), 6u32), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + ParachainStaking::set_collator_commission( + RuntimeOrigin::signed(45), + Perbill::from_percent(5) + ), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + ParachainStaking::set_blocks_per_round(RuntimeOrigin::signed(45), 3u32), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +// SET TOTAL SELECTED + +#[test] +fn set_total_selected_event_emits_correctly() { + ExtBuilder::default().build().execute_with(|| { + // before we can bump total_selected we must bump the blocks per round + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 7u32)); + roll_blocks(1); + assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 6u32)); + assert_events_eq!(Event::TotalSelectedSet { old: 5u32, new: 6u32 }); + }); +} + +#[test] +fn set_total_selected_fails_if_above_blocks_per_round() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(ParachainStaking::round().length, 5); // test relies on this + assert_noop!( + ParachainStaking::set_total_selected(RuntimeOrigin::root(), 6u32), + Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, + ); + }); +} + +#[test] +fn set_total_selected_fails_if_above_max_candidates() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(::MaxCandidates::get(), 200); // test relies on this + assert_noop!( + ParachainStaking::set_total_selected(RuntimeOrigin::root(), 201u32), + Error::::CannotSetAboveMaxCandidates, + ); + }); +} + +#[test] +fn set_total_selected_fails_if_equal_to_blocks_per_round() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + assert_noop!( + ParachainStaking::set_total_selected(RuntimeOrigin::root(), 10u32), + Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, + ); + }); +} + +#[test] +fn set_total_selected_passes_if_below_blocks_per_round() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 9u32)); + }); +} + +#[test] +fn set_blocks_per_round_fails_if_below_total_selected() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 20u32)); + assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 10u32)); + assert_noop!( + ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 9u32), + Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, + ); + }); +} + +#[test] +fn set_blocks_per_round_fails_if_equal_to_total_selected() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 9u32)); + assert_noop!( + ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 9u32), + Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, + ); + }); +} + +#[test] +fn set_blocks_per_round_passes_if_above_total_selected() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(ParachainStaking::round().length, 5); // test relies on this + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + }); +} + +#[test] +fn set_total_selected_storage_updates_correctly() { + ExtBuilder::default().build().execute_with(|| { + // round length must be >= total_selected, so update that first + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + + assert_eq!(ParachainStaking::total_selected(), 5u32); + assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 6u32)); + assert_eq!(ParachainStaking::total_selected(), 6u32); + }); +} + +#[test] +fn cannot_set_total_selected_to_current_total_selected() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_total_selected(RuntimeOrigin::root(), 5u32), + Error::::NoWritingSameValue + ); + }); +} + +#[test] +fn cannot_set_total_selected_below_module_min() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_total_selected(RuntimeOrigin::root(), 4u32), + Error::::CannotSetBelowMin + ); + }); +} + +// SET COLLATOR COMMISSION + +#[test] +fn set_collator_commission_event_emits_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_collator_commission( + RuntimeOrigin::root(), + Perbill::from_percent(5) + )); + assert_events_eq!(Event::CollatorCommissionSet { + old: Perbill::from_percent(20), + new: Perbill::from_percent(5), + }); + }); +} + +#[test] +fn set_collator_commission_storage_updates_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(ParachainStaking::collator_commission(), Perbill::from_percent(20)); + assert_ok!(ParachainStaking::set_collator_commission( + RuntimeOrigin::root(), + Perbill::from_percent(5) + )); + assert_eq!(ParachainStaking::collator_commission(), Perbill::from_percent(5)); + }); +} + +#[test] +fn cannot_set_collator_commission_to_current_collator_commission() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_collator_commission( + RuntimeOrigin::root(), + Perbill::from_percent(20) + ), + Error::::NoWritingSameValue + ); + }); +} + +// SET BLOCKS PER ROUND + +#[test] +fn set_blocks_per_round_event_emits_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + assert_events_eq!(Event::BlocksPerRoundSet { + current_round: 1, + first_block: 0, + old: 5, + new: 6, + new_per_round_inflation_min: Perbill::from_parts(570), + new_per_round_inflation_ideal: Perbill::from_parts(570), + new_per_round_inflation_max: Perbill::from_parts(570), + }); + }); +} + +#[test] +fn set_blocks_per_round_storage_updates_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(ParachainStaking::round().length, 5); + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + assert_eq!(ParachainStaking::round().length, 6); + }); +} + +#[test] +fn cannot_set_blocks_per_round_below_module_min() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 2u32), + Error::::CannotSetBelowMin + ); + }); +} + +#[test] +fn cannot_set_blocks_per_round_to_current_blocks_per_round() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 5u32), + Error::::NoWritingSameValue + ); + }); +} + +#[test] +fn round_immediately_jumps_if_current_duration_exceeds_new_blocks_per_round() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // we can't lower the blocks per round because it must be above the number of collators, + // and we can't lower the number of collators because it must be above + // MinSelectedCandidates. so we first raise blocks per round, then lower it. + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + + roll_to(10); + assert_events_emitted!(Event::NewRound { + starting_block: 10, + round: 2, + selected_collators_number: 1, + total_balance: 20 + },); + roll_to(17); + assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + roll_to(18); + assert_events_emitted!(Event::NewRound { + starting_block: 18, + round: 3, + selected_collators_number: 1, + total_balance: 20 + }); + }); +} + +// ~~ MONETARY GOVERNANCE ~~ + +#[test] +fn invalid_monetary_origin_fails() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_staking_expectations( + RuntimeOrigin::signed(45), + Range { min: 3u32.into(), ideal: 4u32.into(), max: 5u32.into() } + ), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + ParachainStaking::set_inflation( + RuntimeOrigin::signed(45), + Range { + min: Perbill::from_percent(3), + ideal: Perbill::from_percent(4), + max: Perbill::from_percent(5) + } + ), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + ParachainStaking::set_inflation( + RuntimeOrigin::signed(45), + Range { + min: Perbill::from_percent(3), + ideal: Perbill::from_percent(4), + max: Perbill::from_percent(5) + } + ), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + ParachainStaking::set_parachain_bond_account(RuntimeOrigin::signed(45), 11), + sp_runtime::DispatchError::BadOrigin + ); + assert_noop!( + ParachainStaking::set_parachain_bond_reserve_percent( + RuntimeOrigin::signed(45), + Percent::from_percent(2) + ), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +// SET STAKING EXPECTATIONS + +#[test] +fn set_staking_event_emits_event_correctly() { + ExtBuilder::default().build().execute_with(|| { + // valid call succeeds + assert_ok!(ParachainStaking::set_staking_expectations( + RuntimeOrigin::root(), + Range { min: 3u128, ideal: 4u128, max: 5u128 } + )); + assert_events_eq!(Event::StakeExpectationsSet { + expect_min: 3u128, + expect_ideal: 4u128, + expect_max: 5u128, + }); + }); +} + +#[test] +fn set_staking_updates_storage_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!( + ParachainStaking::inflation_config().expect, + Range { min: 700, ideal: 700, max: 700 } + ); + assert_ok!(ParachainStaking::set_staking_expectations( + RuntimeOrigin::root(), + Range { min: 3u128, ideal: 4u128, max: 5u128 } + )); + assert_eq!( + ParachainStaking::inflation_config().expect, + Range { min: 3u128, ideal: 4u128, max: 5u128 } + ); + }); +} + +#[test] +fn cannot_set_invalid_staking_expectations() { + ExtBuilder::default().build().execute_with(|| { + // invalid call fails + assert_noop!( + ParachainStaking::set_staking_expectations( + RuntimeOrigin::root(), + Range { min: 5u128, ideal: 4u128, max: 3u128 } + ), + Error::::InvalidSchedule + ); + }); +} + +#[test] +fn cannot_set_same_staking_expectations() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_staking_expectations( + RuntimeOrigin::root(), + Range { min: 3u128, ideal: 4u128, max: 5u128 } + )); + assert_noop!( + ParachainStaking::set_staking_expectations( + RuntimeOrigin::root(), + Range { min: 3u128, ideal: 4u128, max: 5u128 } + ), + Error::::NoWritingSameValue + ); + }); +} + +// SET INFLATION + +#[test] +fn set_inflation_event_emits_correctly() { + ExtBuilder::default().build().execute_with(|| { + let (min, ideal, max): (Perbill, Perbill, Perbill) = + (Perbill::from_percent(3), Perbill::from_percent(4), Perbill::from_percent(5)); + assert_ok!(ParachainStaking::set_inflation( + RuntimeOrigin::root(), + Range { min, ideal, max } + )); + assert_events_eq!(Event::InflationSet { + annual_min: min, + annual_ideal: ideal, + annual_max: max, + round_min: Perbill::from_parts(28), + round_ideal: Perbill::from_parts(38), + round_max: Perbill::from_parts(47), + }); + }); +} + +#[test] +fn set_inflation_storage_updates_correctly() { + ExtBuilder::default().build().execute_with(|| { + let (min, ideal, max): (Perbill, Perbill, Perbill) = + (Perbill::from_percent(3), Perbill::from_percent(4), Perbill::from_percent(5)); + assert_eq!( + ParachainStaking::inflation_config().annual, + Range { + min: Perbill::from_percent(50), + ideal: Perbill::from_percent(50), + max: Perbill::from_percent(50) + } + ); + assert_eq!( + ParachainStaking::inflation_config().round, + Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5) + } + ); + assert_ok!(ParachainStaking::set_inflation( + RuntimeOrigin::root(), + Range { min, ideal, max } + ),); + assert_eq!(ParachainStaking::inflation_config().annual, Range { min, ideal, max }); + assert_eq!( + ParachainStaking::inflation_config().round, + Range { + min: Perbill::from_parts(28), + ideal: Perbill::from_parts(38), + max: Perbill::from_parts(47) + } + ); + }); +} + +#[test] +fn cannot_set_invalid_inflation() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_inflation( + RuntimeOrigin::root(), + Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(4), + max: Perbill::from_percent(3) + } + ), + Error::::InvalidSchedule + ); + }); +} + +#[test] +fn cannot_set_same_inflation() { + ExtBuilder::default().build().execute_with(|| { + let (min, ideal, max): (Perbill, Perbill, Perbill) = + (Perbill::from_percent(3), Perbill::from_percent(4), Perbill::from_percent(5)); + assert_ok!(ParachainStaking::set_inflation( + RuntimeOrigin::root(), + Range { min, ideal, max } + ),); + assert_noop!( + ParachainStaking::set_inflation(RuntimeOrigin::root(), Range { min, ideal, max }), + Error::::NoWritingSameValue + ); + }); +} + +// SET PARACHAIN BOND ACCOUNT + +#[test] +fn set_parachain_bond_account_event_emits_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); + assert_events_eq!(Event::ParachainBondAccountSet { old: 0, new: 11 }); + }); +} + +#[test] +fn set_parachain_bond_account_storage_updates_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(ParachainStaking::parachain_bond_info().account, 0); + assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); + assert_eq!(ParachainStaking::parachain_bond_info().account, 11); + }); +} + +// SET PARACHAIN BOND RESERVE PERCENT + +#[test] +fn set_parachain_bond_reserve_percent_event_emits_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(ParachainStaking::set_parachain_bond_reserve_percent( + RuntimeOrigin::root(), + Percent::from_percent(50) + )); + assert_events_eq!(Event::ParachainBondReservePercentSet { + old: Percent::from_percent(30), + new: Percent::from_percent(50), + }); + }); +} + +#[test] +fn set_parachain_bond_reserve_percent_storage_updates_correctly() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(ParachainStaking::parachain_bond_info().percent, Percent::from_percent(30)); + assert_ok!(ParachainStaking::set_parachain_bond_reserve_percent( + RuntimeOrigin::root(), + Percent::from_percent(50) + )); + assert_eq!(ParachainStaking::parachain_bond_info().percent, Percent::from_percent(50)); + }); +} + +#[test] +fn cannot_set_same_parachain_bond_reserve_percent() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::set_parachain_bond_reserve_percent( + RuntimeOrigin::root(), + Percent::from_percent(30) + ), + Error::::NoWritingSameValue + ); + }); +} + +// ~~ PUBLIC ~~ + +// JOIN CANDIDATES + +#[test] +fn join_candidates_event_emits_correctly() { + ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); + assert_events_eq!(Event::JoinedCollatorCandidates { + account: 1, + amount_locked: 10u128, + new_total_amt_locked: 10u128, + }); + }); +} + +#[test] +fn join_candidates_reserves_balance() { + ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); + }); +} + +#[test] +fn join_candidates_increases_total_staked() { + ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { + assert_eq!(ParachainStaking::total(), 0); + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); + assert_eq!(ParachainStaking::total(), 10); + }); +} + +#[test] +fn join_candidates_creates_candidate_state() { + ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { + assert!(ParachainStaking::candidate_info(1).is_none()); + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); + let candidate_state = ParachainStaking::candidate_info(1).expect("just joined => exists"); + assert_eq!(candidate_state.bond, 10u128); + }); +} + +#[test] +fn join_candidates_adds_to_candidate_pool() { + ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { + assert!(ParachainStaking::candidate_pool().0.is_empty()); + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); + let candidate_pool = ParachainStaking::candidate_pool(); + assert_eq!(candidate_pool.0[0].owner, 1); + assert_eq!(candidate_pool.0[0].amount, 10); + }); +} + +#[test] +fn cannot_join_candidates_if_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 1000)]) + .with_candidates(vec![(1, 500)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 11u128, 100u32), + Error::::CandidateExists + ); + }); +} + +#[test] +fn cannot_join_candidates_if_delegator() { + ExtBuilder::default() + .with_balances(vec![(1, 50), (2, 20)]) + .with_candidates(vec![(1, 50)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(2), 10u128, 1u32), + Error::::DelegatorExists + ); + }); +} + +#[test] +fn cannot_join_candidates_without_min_bond() { + ExtBuilder::default().with_balances(vec![(1, 1000)]).build().execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 9u128, 100u32), + Error::::CandidateBondBelowMin + ); + }); +} + +#[test] +fn can_force_join_candidates_without_min_bond() { + ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { + assert_ok!(ParachainStaking::force_join_candidates(RuntimeOrigin::root(), 1, 9, 100u32)); + assert_events_eq!(Event::JoinedCollatorCandidates { + account: 1, + amount_locked: 9u128, + new_total_amt_locked: 9u128, + }); + }); +} + +#[test] +fn cannot_join_candidates_with_more_than_available_balance() { + ExtBuilder::default().with_balances(vec![(1, 500)]).build().execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 501u128, 100u32), + DispatchError::Module(ModuleError { + index: 2, + error: [8, 0, 0, 0], + message: Some("InsufficientBalance") + }) + ); + }); +} + +#[test] +fn insufficient_join_candidates_weight_hint_fails() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + for i in 0..5 { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(6), 20, i), + Error::::TooLowCandidateCountWeightHintJoinCandidates + ); + } + }); +} + +#[test] +fn sufficient_join_candidates_weight_hint_succeeds() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 20), + (3, 20), + (4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20), + (9, 20), + ]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + let mut count = 5u32; + for i in 6..10 { + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(i), 20, count)); + count += 1u32; + } + }); +} + +#[test] +fn join_candidates_fails_if_above_max_candidate_count() { + let mut candidates = vec![]; + for i in 1..=crate::mock::MaxCandidates::get() { + candidates.push((i as u64, 80)); + } + + let new_candidate = crate::mock::MaxCandidates::get() as u64 + 1; + let mut balances = candidates.clone(); + balances.push((new_candidate, 100)); + + ExtBuilder::default() + .with_balances(balances) + .with_candidates(candidates) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates( + RuntimeOrigin::signed(new_candidate), + 80, + crate::mock::MaxCandidates::get(), + ), + Error::::CandidateLimitReached, + ); + }); +} + +// SCHEDULE LEAVE CANDIDATES + +#[test] +fn leave_candidates_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_events_eq!(Event::CandidateScheduledExit { + exit_allowed_round: 1, + candidate: 1, + scheduled_exit: 3 + }); + }); +} + +#[test] +fn leave_candidates_removes_candidate_from_candidate_pool() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::candidate_pool().0.len(), 1); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert!(ParachainStaking::candidate_pool().0.is_empty()); + }); +} + +#[test] +fn cannot_leave_candidates_if_not_candidate() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32), + Error::::CandidateDNE + ); + }); +} + +#[test] +fn cannot_leave_candidates_if_already_leaving_candidates() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_noop!( + ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32), + Error::::CandidateAlreadyLeaving + ); + }); +} + +#[test] +fn insufficient_leave_candidates_weight_hint_fails() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + for i in 1..6 { + assert_noop!( + ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(i), 4u32), + Error::::TooLowCandidateCountToLeaveCandidates + ); + } + }); +} + +#[test] +fn enable_marking_offline_works() { + ExtBuilder::default().with_balances(vec![(1, 20)]).build().execute_with(|| { + assert_ok!(ParachainStaking::enable_marking_offline(RuntimeOrigin::root(), true)); + assert!(ParachainStaking::marking_offline()); + + // Set to false now + assert_ok!(ParachainStaking::enable_marking_offline(RuntimeOrigin::root(), false)); + assert!(!ParachainStaking::marking_offline()); + }); +} + +#[test] +fn enable_marking_offline_fails_bad_origin() { + ExtBuilder::default().with_balances(vec![(1, 20)]).build().execute_with(|| { + assert_noop!( + ParachainStaking::enable_marking_offline(RuntimeOrigin::signed(1), true), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn notify_inactive_collator_works() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + // Enable killswitch + >::set(true); + + // Round 2 + roll_to_round_begin(2); + + // Change block author + set_block_author(1); + + // Finalize the first block of round 2 + ParachainStaking::on_finalize(5); + + // We don't produce blocks on round 3 + roll_to_round_begin(3); + roll_blocks(1); + + // We don't produce blocks on round 4 + roll_to_round_begin(4); + roll_blocks(1); + + // Round 6 - notify the collator as inactive + roll_to_round_begin(6); + roll_blocks(1); + + assert_eq!(::MaxOfflineRounds::get(), 1); + assert_eq!(::RewardPaymentDelay::get(), 2); + + // Call 'notify_inactive_collator' extrinsic + assert_ok!(ParachainStaking::notify_inactive_collator(RuntimeOrigin::signed(1), 1)); + + // Check the collator was marked as offline as it hasn't produced blocks + assert_events_eq!(Event::CandidateWentOffline { candidate: 1 },); + }); +} + +#[test] +fn notify_inactive_collator_fails_too_low_collator_count() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20)]) + .build() + .execute_with(|| { + // Enable killswitch + >::set(true); + + // Round 4 + roll_to_round_begin(4); + roll_blocks(1); + + // Call 'notify_inactive_collator' extrinsic + assert_noop!( + ParachainStaking::notify_inactive_collator(RuntimeOrigin::signed(1), 1), + Error::::TooLowCollatorCountToNotifyAsInactive + ); + }); +} + +#[test] +fn notify_inactive_collator_fails_candidate_is_not_collator() { + ExtBuilder::default() + .with_balances(vec![(1, 80), (2, 80), (3, 80), (4, 80), (5, 80), (6, 20)]) + .with_candidates(vec![(1, 80), (2, 80), (3, 80), (4, 80), (5, 80)]) + .build() + .execute_with(|| { + // Enable killswitch + >::set(true); + + set_block_author(1); + + roll_to_round_begin(2); + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 80 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 5, + total_balance: 400, + }, + ); + roll_blocks(1); + + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(6), 10, 100)); + + // Round 6 + roll_to_round_begin(6); + assert_events_eq!( + Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 6, collator_account: 5, total_exposed_amount: 80 }, + Event::NewRound { + starting_block: 25, + round: 6, + selected_collators_number: 5, + total_balance: 400, + }, + ); + roll_blocks(1); + + // A candidate cannot be notified as inactive if it hasn't been selected + // to produce blocks + assert_noop!( + ParachainStaking::notify_inactive_collator(RuntimeOrigin::signed(1), 6), + Error::::CannotBeNotifiedAsInactive + ); + }); +} + +#[test] +fn notify_inactive_collator_fails_cannot_be_notified_as_inactive() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + // Enable killswitch + >::set(true); + + // Round 2 + roll_to_round_begin(2); + + // Change block author + set_block_author(1); + + // Finalize the first block of round 2 + ParachainStaking::on_finalize(5); + + // Round 3 + roll_to_round_begin(3); + roll_blocks(1); + + // Finalize a block of round 3 + ParachainStaking::on_finalize(11); + + // Round 4 + roll_to_round_begin(4); + roll_blocks(1); + + // Call 'notify_inactive_collator' extrinsic + assert_noop!( + ParachainStaking::notify_inactive_collator(RuntimeOrigin::signed(1), 1), + Error::::CannotBeNotifiedAsInactive + ); + }); +} + +#[test] +fn notify_inactive_collator_fails_round_too_low() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + // Enable killswitch + >::set(true); + + // Round 1 + roll_to_round_begin(1); + roll_blocks(1); + + // Call 'notify_inactive_collator' extrinsic + assert_noop!( + ParachainStaking::notify_inactive_collator(RuntimeOrigin::signed(1), 1), + Error::::CurrentRoundTooLow + ); + }); +} + +#[test] +fn sufficient_leave_candidates_weight_hint_succeeds() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20)]) + .build() + .execute_with(|| { + let mut count = 5u32; + for i in 1..6 { + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(i), + count + )); + count -= 1u32; + } + }); +} + +// EXECUTE LEAVE CANDIDATES + +#[test] +fn execute_leave_candidates_emits_event() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_events_emitted!(Event::CandidateLeft { + ex_candidate: 1, + unlocked_amount: 10, + new_total_amt_locked: 0 + }); + }); +} + +#[test] +fn execute_leave_candidates_callable_by_any_signed() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 1, 0)); + }); +} + +#[test] +fn execute_leave_candidates_requires_correct_weight_hint() { + ExtBuilder::default() + .with_balances(vec![(1, 10), (2, 10), (3, 10), (4, 10)]) + .with_candidates(vec![(1, 10)]) + .with_delegations(vec![(2, 1, 10), (3, 1, 10), (4, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + roll_to(10); + for i in 0..3 { + assert_noop!( + ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, i), + Error::::TooLowCandidateDelegationCountToLeaveCandidates + ); + } + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 1, 3)); + }); +} + +#[test] +fn execute_leave_candidates_unreserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); + }); +} + +#[test] +fn execute_leave_candidates_decreases_total_staked() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 10); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_eq!(ParachainStaking::total(), 0); + }); +} + +#[test] +fn execute_leave_candidates_removes_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + // candidate state is not immediately removed + let candidate_state = + ParachainStaking::candidate_info(1).expect("just left => still exists"); + assert_eq!(candidate_state.bond, 10u128); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert!(ParachainStaking::candidate_info(1).is_none()); + }); +} + +#[test] +fn execute_leave_candidates_removes_pending_delegation_requests() { + ExtBuilder::default() + .with_balances(vec![(1, 10), (2, 15)]) + .with_candidates(vec![(1, 10)]) + .with_delegations(vec![(2, 1, 15)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + let state = ParachainStaking::delegation_scheduled_requests(1); + assert_eq!( + state, + vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Decrease(5), + }], + ); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + // candidate state is not immediately removed + let candidate_state = + ParachainStaking::candidate_info(1).expect("just left => still exists"); + assert_eq!(candidate_state.bond, 10u128); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 1)); + assert!(ParachainStaking::candidate_info(1).is_none()); + assert!( + !ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2), + "delegation request not removed" + ); + assert!( + !>::contains_key(1), + "the key was not removed from storage" + ); + }); +} + +#[test] +fn cannot_execute_leave_candidates_before_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_noop!( + ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(3), 1, 0) + .map_err(|err| err.error), + Error::::CandidateCannotLeaveYet + ); + roll_to(9); + assert_noop!( + ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(3), 1, 0) + .map_err(|err| err.error), + Error::::CandidateCannotLeaveYet + ); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(3), 1, 0)); + }); +} + +// CANCEL LEAVE CANDIDATES + +#[test] +fn cancel_leave_candidates_emits_event() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::cancel_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_events_emitted!(Event::CancelledCandidateExit { candidate: 1 }); + }); +} + +#[test] +fn cancel_leave_candidates_updates_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::cancel_leave_candidates(RuntimeOrigin::signed(1), 1)); + let candidate = + ParachainStaking::candidate_info(1).expect("just cancelled leave so exists"); + assert!(candidate.is_active()); + }); +} + +#[test] +fn cancel_leave_candidates_adds_to_candidate_pool() { + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::cancel_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); + assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 10); + }); +} + +// GO OFFLINE + +#[test] +fn go_offline_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + assert_events_eq!(Event::CandidateWentOffline { candidate: 1 }); + }); +} + +#[test] +fn go_offline_removes_candidate_from_candidate_pool() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::candidate_pool().0.len(), 1); + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + assert!(ParachainStaking::candidate_pool().0.is_empty()); + }); +} + +#[test] +fn go_offline_updates_candidate_state_to_idle() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + let candidate_state = ParachainStaking::candidate_info(1).expect("is active candidate"); + assert_eq!(candidate_state.status, CollatorStatus::Active); + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + let candidate_state = + ParachainStaking::candidate_info(1).expect("is candidate, just offline"); + assert_eq!(candidate_state.status, CollatorStatus::Idle); + }); +} + +#[test] +fn cannot_go_offline_if_not_candidate() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::go_offline(RuntimeOrigin::signed(3)).map_err(|err| err.error), + Error::::CandidateDNE + ); + }); +} + +#[test] +fn cannot_go_offline_if_already_offline() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + assert_noop!( + ParachainStaking::go_offline(RuntimeOrigin::signed(1)).map_err(|err| err.error), + Error::::AlreadyOffline + ); + }); +} + +// GO ONLINE + +#[test] +fn go_online_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + roll_blocks(1); + assert_ok!(ParachainStaking::go_online(RuntimeOrigin::signed(1))); + assert_events_eq!(Event::CandidateBackOnline { candidate: 1 }); + }); +} + +#[test] +fn go_online_adds_to_candidate_pool() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + assert!(ParachainStaking::candidate_pool().0.is_empty()); + assert_ok!(ParachainStaking::go_online(RuntimeOrigin::signed(1))); + assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); + assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 20); + }); +} + +#[test] +fn go_online_storage_updates_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(1))); + let candidate_state = + ParachainStaking::candidate_info(1).expect("offline still exists"); + assert_eq!(candidate_state.status, CollatorStatus::Idle); + assert_ok!(ParachainStaking::go_online(RuntimeOrigin::signed(1))); + let candidate_state = ParachainStaking::candidate_info(1).expect("online so exists"); + assert_eq!(candidate_state.status, CollatorStatus::Active); + }); +} + +#[test] +fn cannot_go_online_if_not_candidate() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::go_online(RuntimeOrigin::signed(3)), + Error::::CandidateDNE + ); + }); +} + +#[test] +fn cannot_go_online_if_already_online() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::go_online(RuntimeOrigin::signed(1)).map_err(|err| err.error), + Error::::AlreadyActive + ); + }); +} + +#[test] +fn cannot_go_online_if_leaving() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_noop!( + ParachainStaking::go_online(RuntimeOrigin::signed(1)).map_err(|err| err.error), + Error::::CannotGoOnlineIfLeaving + ); + }); +} + +// CANDIDATE BOND MORE + +#[test] +fn candidate_bond_more_emits_correct_event() { + ExtBuilder::default() + .with_balances(vec![(1, 50)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_events_eq!(Event::CandidateBondedMore { + candidate: 1, + amount: 30, + new_total_bond: 50 + }); + }); +} + +#[test] +fn candidate_bond_more_reserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 50)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 30); + assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); + }); +} + +#[test] +fn candidate_bond_more_increases_total() { + ExtBuilder::default() + .with_balances(vec![(1, 50)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + let mut total = ParachainStaking::total(); + assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + total += 30; + assert_eq!(ParachainStaking::total(), total); + }); +} + +#[test] +fn candidate_bond_more_updates_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 50)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); + assert_eq!(candidate_state.bond, 20); + assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); + assert_eq!(candidate_state.bond, 50); + }); +} + +#[test] +fn candidate_bond_more_updates_candidate_pool() { + ExtBuilder::default() + .with_balances(vec![(1, 50)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); + assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 20); + assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); + assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 50); + }); +} + +// SCHEDULE CANDIDATE BOND LESS + +#[test] +fn schedule_candidate_bond_less_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + assert_events_eq!(Event::CandidateBondLessRequested { + candidate: 1, + amount_to_decrease: 10, + execute_round: 3, + }); + }); +} + +#[test] +fn cannot_schedule_candidate_bond_less_if_request_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 5)); + assert_noop!( + ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 5), + Error::::PendingCandidateRequestAlreadyExists + ); + }); +} + +#[test] +fn cannot_schedule_candidate_bond_less_if_not_candidate() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(6), 50), + Error::::CandidateDNE + ); + }); +} + +#[test] +fn cannot_schedule_candidate_bond_less_if_new_total_below_min_candidate_stk() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 21), + Error::::CandidateBondBelowMin + ); + }); +} + +#[test] +fn can_schedule_candidate_bond_less_if_leaving_candidates() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + }); +} + +#[test] +fn cannot_schedule_candidate_bond_less_if_exited_candidates() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_noop!( + ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 10), + Error::::CandidateDNE + ); + }); +} + +// 2. EXECUTE BOND LESS REQUEST + +#[test] +fn execute_candidate_bond_less_emits_correct_event() { + ExtBuilder::default() + .with_balances(vec![(1, 50)]) + .with_candidates(vec![(1, 50)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 30 + )); + roll_to(10); + roll_blocks(1); + assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_events_eq!(Event::CandidateBondedLess { + candidate: 1, + amount: 30, + new_bond: 20 + }); + }); +} + +#[test] +fn execute_candidate_bond_less_unreserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); + }); +} + +#[test] +fn execute_candidate_bond_less_decreases_total() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + let mut total = ParachainStaking::total(); + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + total -= 10; + assert_eq!(ParachainStaking::total(), total); + }); +} + +#[test] +fn execute_candidate_bond_less_updates_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); + assert_eq!(candidate_state.bond, 30); + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); + assert_eq!(candidate_state.bond, 20); + }); +} + +#[test] +fn execute_candidate_bond_less_updates_candidate_pool() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); + assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 30); + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); + assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 20); + }); +} + +// CANCEL CANDIDATE BOND LESS REQUEST + +#[test] +fn cancel_candidate_bond_less_emits_event() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + assert_ok!(ParachainStaking::cancel_candidate_bond_less(RuntimeOrigin::signed(1))); + assert_events_emitted!(Event::CancelledCandidateBondLess { + candidate: 1, + amount: 10, + execute_round: 3, + }); + }); +} + +#[test] +fn cancel_candidate_bond_less_updates_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + assert_ok!(ParachainStaking::cancel_candidate_bond_less(RuntimeOrigin::signed(1))); + assert!(ParachainStaking::candidate_info(1).unwrap().request.is_none()); + }); +} + +#[test] +fn only_candidate_can_cancel_candidate_bond_less_request() { + ExtBuilder::default() + .with_balances(vec![(1, 30)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 10 + )); + assert_noop!( + ParachainStaking::cancel_candidate_bond_less(RuntimeOrigin::signed(2)), + Error::::CandidateDNE + ); + }); +} + +// DELEGATE + +#[test] +fn delegate_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + assert_events_eq!(Event::Delegation { + delegator: 2, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 40 }, + auto_compound: Percent::zero(), + }); + }); +} + +#[test] +fn delegate_reserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + }); +} + +#[test] +fn delegate_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert!(ParachainStaking::delegator_state(2).is_none()); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + let delegator_state = + ParachainStaking::delegator_state(2).expect("just delegated => exists"); + assert_eq!(delegator_state.total(), 10); + assert_eq!(delegator_state.delegations.0[0].owner, 1); + assert_eq!(delegator_state.delegations.0[0].amount, 10); + }); +} + +#[test] +fn delegate_updates_collator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + let candidate_state = + ParachainStaking::candidate_info(1).expect("registered in genesis"); + assert_eq!(candidate_state.total_counted, 30); + let top_delegations = + ParachainStaking::top_delegations(1).expect("registered in genesis"); + assert!(top_delegations.delegations.is_empty()); + assert!(top_delegations.total.is_zero()); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + let candidate_state = + ParachainStaking::candidate_info(1).expect("just delegated => exists"); + assert_eq!(candidate_state.total_counted, 40); + let top_delegations = + ParachainStaking::top_delegations(1).expect("just delegated => exists"); + assert_eq!(top_delegations.delegations[0].owner, 2); + assert_eq!(top_delegations.delegations[0].amount, 10); + assert_eq!(top_delegations.total, 10); + }); +} + +#[test] +fn can_delegate_immediately_after_other_join_candidates() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 20, 0)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 20, 0, 0)); + }); +} + +#[test] +fn can_delegate_if_revoking() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 4, 10, 0, 2)); + }); +} + +#[test] +fn cannot_delegate_if_full_and_new_delegation_less_than_or_equal_lowest_bottom() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 10), + (3, 10), + (4, 10), + (5, 10), + (6, 10), + (7, 10), + (8, 10), + (9, 10), + (10, 10), + (11, 10), + ]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 10), + (4, 1, 10), + (5, 1, 10), + (6, 1, 10), + (8, 1, 10), + (9, 1, 10), + (10, 1, 10), + ]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(11), 1, 10, 8, 0), + Error::::CannotDelegateLessThanOrEqualToLowestBottomWhenFull + ); + }); +} + +#[test] +fn can_delegate_if_full_and_new_delegation_greater_than_lowest_bottom() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 10), + (3, 10), + (4, 10), + (5, 10), + (6, 10), + (7, 10), + (8, 10), + (9, 10), + (10, 10), + (11, 11), + ]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 10), + (4, 1, 10), + (5, 1, 10), + (6, 1, 10), + (8, 1, 10), + (9, 1, 10), + (10, 1, 10), + ]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(11), 1, 11, 8, 0)); + assert_events_emitted!(Event::DelegationKicked { + delegator: 10, + candidate: 1, + unstaked_amount: 10 + }); + assert_events_emitted!(Event::DelegatorLeft { delegator: 10, unstaked_amount: 10 }); + }); +} + +#[test] +fn can_still_delegate_if_leaving() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 20), (3, 20)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 3, 10, 0, 1),); + }); +} + +#[test] +fn cannot_delegate_if_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30)]) + .with_candidates(vec![(1, 20), (2, 20)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0), + Error::::CandidateExists + ); + }); +} + +#[test] +fn cannot_delegate_if_already_delegated() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30)]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![(2, 1, 20)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 1, 1), + Error::::AlreadyDelegatedCandidate + ); + }); +} + +#[test] +fn cannot_delegate_more_than_max_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 50), (3, 20), (4, 20), (5, 20), (6, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10), (2, 4, 10), (2, 5, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(2), 6, 10, 0, 4), + Error::::ExceedMaxDelegationsPerDelegator, + ); + }); +} + +#[test] +fn sufficient_delegate_weight_hint_succeeds() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 20), + (3, 20), + (4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20), + (9, 20), + (10, 20), + ]) + .with_candidates(vec![(1, 20), (2, 20)]) + .with_delegations(vec![(3, 1, 10), (4, 1, 10), (5, 1, 10), (6, 1, 10)]) + .build() + .execute_with(|| { + let mut count = 4u32; + for i in 7..11 { + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(i), + 1, + 10, + count, + 0u32 + )); + count += 1u32; + } + for (count, i) in (3..11).enumerate() { + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(i), + 2, + 10, + count as u32, + 1u32 + )); + } + }); +} + +#[test] +fn insufficient_delegate_weight_hint_fails() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 20), + (3, 20), + (4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20), + (9, 20), + (10, 20), + ]) + .with_candidates(vec![(1, 20), (2, 20)]) + .with_delegations(vec![(3, 1, 10), (4, 1, 10), (5, 1, 10), (6, 1, 10)]) + .build() + .execute_with(|| { + let mut count = 3u32; + for i in 7..11 { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(i), 1, 10, count, 0u32), + Error::::TooLowCandidateDelegationCountToDelegate + ); + } + // to set up for next error test + count = 4u32; + for i in 7..11 { + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(i), + 1, + 10, + count, + 0u32 + )); + count += 1u32; + } + count = 0u32; + for i in 3..11 { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(i), 2, 10, count, 0u32), + Error::::TooLowDelegationCountToDelegate + ); + count += 1u32; + } + }); +} + +// SCHEDULE REVOKE DELEGATION + +#[test] +fn revoke_delegation_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_events_eq!(Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }); + roll_to_round_begin(3); + roll_blocks(1); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_events_eq!( + Event::DelegatorLeftCandidate { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 30 + }, + Event::DelegationRevoked { delegator: 2, candidate: 1, unstaked_amount: 10 }, + ); + }); +} + +#[test] +fn can_revoke_delegation_if_revoking_another_delegation() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + // this is an exit implicitly because last delegation revoked + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3)); + }); +} + +#[test] +fn cannot_revoke_delegation_if_not_delegator() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1), + Error::::DelegatorDNE + ); + }); +} + +#[test] +fn cannot_revoke_delegation_that_dne() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3), + Error::::DelegationDNE + ); + }); +} + +#[test] +fn can_schedule_revoke_delegation_below_min_delegator_stake() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 8), (3, 20)]) + .with_candidates(vec![(1, 20), (3, 20)]) + .with_delegations(vec![(2, 1, 5), (2, 3, 3)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + }); +} + +// DELEGATOR BOND MORE + +#[test] +fn delegator_bond_more_reserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 5); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + }); +} + +#[test] +fn delegator_bond_more_increases_total_staked() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 40); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_eq!(ParachainStaking::total(), 45); + }); +} + +#[test] +fn delegator_bond_more_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 10); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 15); + }); +} + +#[test] +fn delegator_bond_more_updates_candidate_state_top_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 10); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().total, 10); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 15); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().total, 15); + }); +} + +#[test] +fn delegator_bond_more_updates_candidate_state_bottom_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10), (3, 1, 20), (4, 1, 20), (5, 1, 20), (6, 1, 20)]) + .build() + .execute_with(|| { + assert_eq!( + ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].owner, + 2 + ); + assert_eq!( + ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].amount, + 10 + ); + assert_eq!(ParachainStaking::bottom_delegations(1).unwrap().total, 10); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_events_eq!(Event::DelegationIncreased { + delegator: 2, + candidate: 1, + amount: 5, + in_top: false + }); + assert_eq!( + ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].owner, + 2 + ); + assert_eq!( + ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].amount, + 15 + ); + assert_eq!(ParachainStaking::bottom_delegations(1).unwrap().total, 15); + }); +} + +#[test] +fn delegator_bond_more_increases_total() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 40); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_eq!(ParachainStaking::total(), 45); + }); +} + +#[test] +fn can_delegator_bond_more_for_leaving_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + }); +} + +#[test] +fn delegator_bond_more_disallowed_when_revoke_scheduled() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_noop!( + ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5), + >::PendingDelegationRevoke + ); + }); +} + +#[test] +fn delegator_bond_more_allowed_when_bond_decrease_scheduled() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 15)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5, + )); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + }); +} + +// DELEGATOR BOND LESS + +#[test] +fn delegator_bond_less_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + assert_events_eq!(Event::DelegationDecreaseScheduled { + delegator: 2, + candidate: 1, + amount_to_decrease: 5, + execute_round: 3, + }); + }); +} + +#[test] +fn delegator_bond_less_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + let state = ParachainStaking::delegation_scheduled_requests(1); + assert_eq!( + state, + vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Decrease(5), + }], + ); + }); +} + +#[test] +fn cannot_delegator_bond_less_if_revoking() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 1) + .map_err(|err| err.error), + Error::::PendingDelegationRequestAlreadyExists + ); + }); +} + +#[test] +fn cannot_delegator_bond_less_if_not_delegator() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 5) + .map_err(|err| err.error), + Error::::DelegatorDNE + ); + }); +} + +#[test] +fn cannot_delegator_bond_less_if_candidate_dne() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 3, 5) + .map_err(|err| err.error), + Error::::DelegationDNE + ); + }); +} + +#[test] +fn cannot_delegator_bond_less_if_delegation_dne() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 3, 5) + .map_err(|err| err.error), + Error::::DelegationDNE + ); + }); +} + +#[test] +fn cannot_delegator_bond_less_more_than_total_delegation() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 11) + .map_err(|err| err.error), + Error::::DelegatorBondBelowMin + ); + }); +} + +#[test] +fn cannot_delegator_bond_less_below_min_delegation() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 8) + .map_err(|err| err.error), + Error::::DelegationBelowMin + ); + }); +} + +// EXECUTE PENDING DELEGATION REQUEST + +// 1. REVOKE DELEGATION + +#[test] +fn execute_revoke_delegation_emits_exit_event_if_exit_happens() { + // last delegation is revocation + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_events_emitted!(Event::DelegatorLeftCandidate { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 30 + }); + assert_events_emitted!(Event::DelegatorLeft { delegator: 2, unstaked_amount: 10 }); + }); +} + +#[test] +fn revoke_delegation_executes_exit_if_last_delegation() { + // last delegation is revocation + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_events_emitted!(Event::DelegatorLeftCandidate { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 30 + }); + assert_events_emitted!(Event::DelegatorLeft { delegator: 2, unstaked_amount: 10 }); + }); +} + +#[test] +fn execute_revoke_delegation_emits_correct_event() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_events_emitted!(Event::DelegatorLeftCandidate { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 30 + }); + }); +} + +#[test] +fn execute_revoke_delegation_unreserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + }); +} + +#[test] +fn execute_revoke_delegation_adds_revocation_to_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert!(!ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert!(ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2)); + }); +} + +#[test] +fn execute_revoke_delegation_removes_revocation_from_delegator_state_upon_execution() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert!(!ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2)); + }); +} + +#[test] +fn execute_revoke_delegation_removes_revocation_from_state_for_single_delegation_leave() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert!( + !ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2), + "delegation was not removed" + ); + }); +} + +#[test] +fn execute_revoke_delegation_decreases_total_staked() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 40); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::total(), 30); + }); +} + +#[test] +fn execute_revoke_delegation_for_last_delegation_removes_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert!(ParachainStaking::delegator_state(2).is_some()); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + // this will be confusing for people + // if status is leaving, then execute_delegation_request works if last delegation + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert!(ParachainStaking::delegator_state(2).is_none()); + }); +} + +#[test] +fn execute_revoke_delegation_removes_delegation_from_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::candidate_info(1).expect("exists").delegation_count, 1u32); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert!(ParachainStaking::candidate_info(1) + .expect("exists") + .delegation_count + .is_zero()); + }); +} + +#[test] +fn can_execute_revoke_delegation_for_leaving_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + // can execute delegation request for leaving candidate + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + }); +} + +#[test] +fn can_execute_leave_candidates_if_revoking_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + // revocation executes during execute leave candidates (callable by anyone) + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 1)); + assert!(!ParachainStaking::is_delegator(&2)); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 10); + }); +} + +#[test] +fn delegator_bond_more_after_revoke_delegation_does_not_effect_exit() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 30), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 3, 10)); + roll_to(100); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert!(ParachainStaking::is_delegator(&2)); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + }); +} + +#[test] +fn delegator_bond_less_after_revoke_delegation_does_not_effect_exit() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 30), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_events_eq!(Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }); + assert_noop!( + ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 2) + .map_err(|err| err.error), + Error::::PendingDelegationRequestAlreadyExists + ); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 3, + 2 + )); + roll_to(10); + roll_blocks(1); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 3 + )); + assert_events_eq!( + Event::DelegatorLeftCandidate { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 30, + }, + Event::DelegationRevoked { delegator: 2, candidate: 1, unstaked_amount: 10 }, + Event::DelegationDecreased { delegator: 2, candidate: 3, amount: 2, in_top: true }, + ); + assert!(ParachainStaking::is_delegator(&2)); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 22); + }); +} + +// 2. EXECUTE BOND LESS + +#[test] +fn execute_delegator_bond_less_unreserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 5); + }); +} + +#[test] +fn execute_delegator_bond_less_decreases_total_staked() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 40); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::total(), 35); + }); +} + +#[test] +fn execute_delegator_bond_less_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 10); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 5); + }); +} + +#[test] +fn execute_delegator_bond_less_updates_candidate_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 10); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); + assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 5); + }); +} + +#[test] +fn execute_delegator_bond_less_decreases_total() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 40); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!(ParachainStaking::total(), 35); + }); +} + +#[test] +fn execute_delegator_bond_less_updates_just_bottom_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 10), (3, 11), (4, 12), (5, 14), (6, 15)]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![(2, 1, 10), (3, 1, 11), (4, 1, 12), (5, 1, 14), (6, 1, 15)]) + .build() + .execute_with(|| { + let pre_call_candidate_info = + ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + let pre_call_top_delegations = + ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + let pre_call_bottom_delegations = + ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 2 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + let post_call_candidate_info = + ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + let post_call_top_delegations = + ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + let post_call_bottom_delegations = + ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + let mut not_equal = false; + for Bond { owner, amount } in pre_call_bottom_delegations.delegations { + for Bond { owner: post_owner, amount: post_amount } in + &post_call_bottom_delegations.delegations + { + if &owner == post_owner && &amount != post_amount { + not_equal = true; + break; + } + } + } + assert!(not_equal); + let mut equal = true; + for Bond { owner, amount } in pre_call_top_delegations.delegations { + for Bond { owner: post_owner, amount: post_amount } in + &post_call_top_delegations.delegations + { + if &owner == post_owner && &amount != post_amount { + equal = false; + break; + } + } + } + assert!(equal); + assert_eq!( + pre_call_candidate_info.total_counted, + post_call_candidate_info.total_counted + ); + }); +} + +#[test] +fn execute_delegator_bond_less_does_not_delete_bottom_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 10), (3, 11), (4, 12), (5, 14), (6, 15)]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![(2, 1, 10), (3, 1, 11), (4, 1, 12), (5, 1, 14), (6, 1, 15)]) + .build() + .execute_with(|| { + let pre_call_candidate_info = + ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + let pre_call_top_delegations = + ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + let pre_call_bottom_delegations = + ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(6), + 1, + 4 + )); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(6), + 6, + 1 + )); + let post_call_candidate_info = + ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + let post_call_top_delegations = + ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + let post_call_bottom_delegations = + ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + let mut equal = true; + for Bond { owner, amount } in pre_call_bottom_delegations.delegations { + for Bond { owner: post_owner, amount: post_amount } in + &post_call_bottom_delegations.delegations + { + if &owner == post_owner && &amount != post_amount { + equal = false; + break; + } + } + } + assert!(equal); + let mut not_equal = false; + for Bond { owner, amount } in pre_call_top_delegations.delegations { + for Bond { owner: post_owner, amount: post_amount } in + &post_call_top_delegations.delegations + { + if &owner == post_owner && &amount != post_amount { + not_equal = true; + break; + } + } + } + assert!(not_equal); + assert_eq!( + pre_call_candidate_info.total_counted - 4, + post_call_candidate_info.total_counted + ); + }); +} + +#[test] +fn can_execute_delegator_bond_less_for_leaving_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 15)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + roll_to(10); + // can execute bond more delegation request for leaving candidate + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + }); +} + +// CANCEL PENDING DELEGATION REQUEST +// 1. CANCEL REVOKE DELEGATION + +#[test] +fn cancel_revoke_delegation_emits_correct_event() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert_events_emitted!(Event::CancelledDelegationRequest { + delegator: 2, + collator: 1, + cancelled_request: CancelledScheduledRequest { + when_executable: 3, + action: DelegationAction::Revoke(10), + }, + }); + }); +} + +#[test] +fn cancel_revoke_delegation_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + let state = ParachainStaking::delegation_scheduled_requests(1); + assert_eq!( + state, + vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Revoke(10), + }], + ); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 10 + ); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert!(!ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2)); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 0 + ); + }); +} + +// 2. CANCEL DELEGATOR BOND LESS + +#[test] +fn cancel_delegator_bond_less_correct_event() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 15)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert_events_emitted!(Event::CancelledDelegationRequest { + delegator: 2, + collator: 1, + cancelled_request: CancelledScheduledRequest { + when_executable: 3, + action: DelegationAction::Decrease(5), + }, + }); + }); +} + +#[test] +fn cancel_delegator_bond_less_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 15)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 15)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 5 + )); + let state = ParachainStaking::delegation_scheduled_requests(1); + assert_eq!( + state, + vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Decrease(5), + }], + ); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 5 + ); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert!(!ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2)); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 0 + ); + }); +} + +// ~~ PROPERTY-BASED TESTS ~~ + +#[test] +fn delegator_schedule_revocation_total() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20), (5, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20), (5, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10), (2, 4, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 10 + ); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 0 + ); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 5, 10, 0, 2)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 4)); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 20, + ); + roll_to(20); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 3 + )); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 10, + ); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 4 + )); + assert_eq!( + ParachainStaking::delegator_state(2) + .map(|x| x.less_total) + .expect("delegator state must exist"), + 0 + ); + }); +} + +#[ignore] +#[test] +fn parachain_bond_inflation_reserve_matches_config() { + 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), + (11, 1), + ]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) + .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .build() + .execute_with(|| { + assert_eq!(Balances::free_balance(11), 1); + // set parachain bond account so DefaultParachainBondReservePercent = 30% of inflation + // is allocated to this account hereafter + assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); + assert_events_eq!(Event::ParachainBondAccountSet { old: 0, new: 11 }); + roll_to_round_begin(2); + // chooses top TotalSelectedCandidates (5), in order + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 5, + total_balance: 140, + }, + ); + assert_eq!(Balances::free_balance(11), 1); + // ~ set block author as 1 for all blocks this round + set_author(2, 1, 100); + roll_to_round_begin(4); + // distribute total issuance to collator 1 and its delegators 6, 7, 19 + assert_eq!(Balances::free_balance(11), 16); + // ~ set block author as 1 for all blocks this round + set_author(3, 1, 100); + set_author(4, 1, 100); + set_author(5, 1, 100); + // 1. ensure delegators are paid for 2 rounds after they leave + assert_noop!( + ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(66), 1), + Error::::DelegatorDNE + ); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(6), 1,)); + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 15 }, + Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 4, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 15, + round: 4, + selected_collators_number: 5, + total_balance: 140, + }, + Event::DelegatorExitScheduled { round: 4, delegator: 6, scheduled_exit: 6 }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 20 }, + Event::Rewarded { account: 6, rewards: 5 }, + Event::Rewarded { account: 7, rewards: 5 }, + Event::Rewarded { account: 10, rewards: 5 }, + ); + // fast forward to block in which delegator 6 exit executes + roll_to_round_begin(5); + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 16 }, + Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 5, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 5, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 5, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 20, + round: 5, + selected_collators_number: 5, + total_balance: 140, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 21 }, + Event::Rewarded { account: 6, rewards: 5 }, + Event::Rewarded { account: 7, rewards: 5 }, + Event::Rewarded { account: 10, rewards: 5 }, + ); + roll_to_round_begin(6); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(6), + 6, + 10 + )); + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 16 }, + Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 6, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 25, + round: 6, + selected_collators_number: 5, + total_balance: 140, + }, + Event::DelegatorLeftCandidate { + delegator: 6, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 40, + }, + Event::DelegatorLeft { delegator: 6, unstaked_amount: 10 }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 22 }, + Event::Rewarded { account: 6, rewards: 6 }, + Event::Rewarded { account: 7, rewards: 6 }, + Event::Rewarded { account: 10, rewards: 6 }, + ); + roll_to_round_begin(7); + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 17 }, + Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 7, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 7, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 30, + round: 7, + selected_collators_number: 5, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 26 }, + Event::Rewarded { account: 7, rewards: 7 }, + Event::Rewarded { account: 10, rewards: 7 }, + ); + assert_eq!(Balances::free_balance(11), 65); + roll_blocks(1); + assert_ok!(ParachainStaking::set_parachain_bond_reserve_percent( + RuntimeOrigin::root(), + Percent::from_percent(50) + )); + assert_events_eq!(Event::ParachainBondReservePercentSet { + old: Percent::from_percent(30), + new: Percent::from_percent(50), + }); + // 6 won't be paid for this round because they left already + set_author(6, 1, 100); + roll_to_round_begin(8); + // keep paying 6 + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 30 }, + Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 8, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 35, + round: 8, + selected_collators_number: 5, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 21 }, + Event::Rewarded { account: 7, rewards: 5 }, + Event::Rewarded { account: 10, rewards: 5 }, + ); + assert_eq!(Balances::free_balance(11), 95); + set_author(7, 1, 100); + roll_to_round_begin(9); + // no more paying 6 + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 32 }, + Event::CollatorChosen { round: 9, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 9, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 9, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 9, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 9, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 40, + round: 9, + selected_collators_number: 5, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 22 }, + Event::Rewarded { account: 7, rewards: 5 }, + Event::Rewarded { account: 10, rewards: 5 }, + ); + assert_eq!(Balances::free_balance(11), 127); + set_author(8, 1, 100); + roll_blocks(1); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); + assert_events_eq!(Event::Delegation { + delegator: 8, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 50 }, + auto_compound: Percent::zero(), + }); + roll_to_round_begin(10); + // new delegation is not rewarded yet + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 33 }, + Event::CollatorChosen { round: 10, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 10, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 10, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 10, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 10, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 45, + round: 10, + selected_collators_number: 5, + total_balance: 140, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 23 }, + Event::Rewarded { account: 7, rewards: 5 }, + Event::Rewarded { account: 10, rewards: 5 }, + ); + assert_eq!(Balances::free_balance(11), 160); + set_author(9, 1, 100); + set_author(10, 1, 100); + roll_to_round_begin(11); + // new delegation is still not rewarded yet + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 35 }, + Event::CollatorChosen { round: 11, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 11, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 11, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 11, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 11, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 50, + round: 11, + selected_collators_number: 5, + total_balance: 140, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 24 }, + Event::Rewarded { account: 7, rewards: 5 }, + Event::Rewarded { account: 10, rewards: 5 }, + ); + assert_eq!(Balances::free_balance(11), 195); + roll_to_round_begin(12); + // new delegation is rewarded, 2 rounds after joining (`RewardPaymentDelay` is 2) + assert_events_eq!( + Event::ReservedForParachainBond { account: 11, value: 37 }, + Event::CollatorChosen { round: 12, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 12, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 12, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 12, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 12, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 55, + round: 12, + selected_collators_number: 5, + total_balance: 140, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 24 }, + Event::Rewarded { account: 7, rewards: 4 }, + Event::Rewarded { account: 10, rewards: 4 }, + Event::Rewarded { account: 8, rewards: 4 }, + ); + assert_eq!(Balances::free_balance(11), 232); + }); +} + +#[test] +fn paid_collator_commission_matches_config() { + ExtBuilder::default() + .with_balances(vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100)]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![(2, 1, 10), (3, 1, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + roll_to_round_begin(2); + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(4), 20u128, 100u32)); + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 40 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 1, + total_balance: 40, + }, + Event::JoinedCollatorCandidates { + account: 4, + amount_locked: 20, + new_total_amt_locked: 60, + }, + ); + + roll_blocks(1); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(5), 4, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 4, 10, 10, 10)); + assert_events_eq!( + Event::Delegation { + delegator: 5, + locked_amount: 10, + candidate: 4, + delegator_position: DelegatorAdded::AddedToTop { new_total: 30 }, + auto_compound: Percent::zero(), + }, + Event::Delegation { + delegator: 6, + locked_amount: 10, + candidate: 4, + delegator_position: DelegatorAdded::AddedToTop { new_total: 40 }, + auto_compound: Percent::zero(), + }, + ); + + roll_to_round_begin(3); + assert_events_eq!( + Event::CollatorChosen { round: 3, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 3, collator_account: 4, total_exposed_amount: 40 }, + Event::NewRound { + starting_block: 10, + round: 3, + selected_collators_number: 2, + total_balance: 80, + }, + ); + // only reward author with id 4 + set_author(3, 4, 100); + roll_to_round_begin(5); + // 20% of 10 is commission + due_portion (0) = 2 + 4 = 6 + // all delegator payouts are 10-2 = 8 * stake_pct + assert_events_eq!( + Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 40 }, + Event::NewRound { + starting_block: 20, + round: 5, + selected_collators_number: 2, + total_balance: 80, + }, + ); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 4, rewards: 21 }, + Event::Rewarded { account: 5, rewards: 7 }, + Event::Rewarded { account: 6, rewards: 7 }, + ); + }); +} + +#[test] +fn collator_exit_executes_after_delay() { + ExtBuilder::default() + .with_balances(vec![ + (1, 1000), + (2, 300), + (3, 100), + (4, 100), + (5, 100), + (6, 100), + (7, 100), + (8, 9), + (9, 4), + ]) + .with_candidates(vec![(1, 500), (2, 200)]) + .with_delegations(vec![(3, 1, 100), (4, 1, 100), (5, 2, 100), (6, 2, 100)]) + .build() + .execute_with(|| { + roll_to(11); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(2), 2)); + assert_events_eq!(Event::CandidateScheduledExit { + exit_allowed_round: 3, + candidate: 2, + scheduled_exit: 5, + }); + let info = ParachainStaking::candidate_info(2).unwrap(); + assert_eq!(info.status, CollatorStatus::Leaving(5)); + roll_to(21); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 2, 2)); + // we must exclude leaving collators from rewards while + // holding them retroactively accountable for previous faults + // (within the last T::SlashingWindow blocks) + assert_events_eq!(Event::CandidateLeft { + ex_candidate: 2, + unlocked_amount: 400, + new_total_amt_locked: 700, + },); + }); +} + +#[test] +fn collator_selection_chooses_top_candidates() { + ExtBuilder::default() + .with_balances(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, 1000), + (6, 1000), + (7, 33), + (8, 33), + (9, 33), + ]) + .with_candidates(vec![(1, 100), (2, 90), (3, 80), (4, 70), (5, 60), (6, 50)]) + .build() + .execute_with(|| { + roll_to_round_begin(2); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(6), 6)); + // should choose top TotalSelectedCandidates (5), in order + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 100 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 90 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 70 }, + Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 60 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 5, + total_balance: 400, + }, + Event::CandidateScheduledExit { + exit_allowed_round: 2, + candidate: 6, + scheduled_exit: 4 + }, + ); + roll_to_round_begin(4); + roll_blocks(1); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(6), 6, 0)); + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(6), 69u128, 100u32)); + assert_events_eq!( + Event::CandidateLeft { + ex_candidate: 6, + unlocked_amount: 50, + new_total_amt_locked: 400, + }, + Event::JoinedCollatorCandidates { + account: 6, + amount_locked: 69u128, + new_total_amt_locked: 469u128, + }, + ); + roll_to_round_begin(6); + // should choose top TotalSelectedCandidates (5), in order + assert_events_eq!( + Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 100 }, + Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 90 }, + Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 70 }, + Event::CollatorChosen { round: 6, collator_account: 6, total_exposed_amount: 69 }, + Event::NewRound { + starting_block: 25, + round: 6, + selected_collators_number: 5, + total_balance: 409, + }, + ); + }); +} + +#[test] +fn payout_distribution_to_solo_collators() { + ExtBuilder::default() + .with_balances(vec![(1, 1000), (2, 1000), (3, 1000), (4, 1000), (7, 33), (8, 33), (9, 33)]) + .with_candidates(vec![(1, 100), (2, 90), (3, 80), (4, 70)]) + .with_rewards_account(999, 100000) + .build() + .execute_with(|| { + roll_to_round_begin(2); + // should choose top TotalCandidatesSelected (5), in order + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 100 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 90 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 70 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 4, + total_balance: 340, + }, + ); + // ~ set block author as 1 for all blocks this round + set_author(2, 1, 100); + roll_to_round_begin(4); + assert_events_eq!( + Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 100 }, + Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 90 }, + Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 70 }, + Event::NewRound { + starting_block: 15, + round: 4, + selected_collators_number: 4, + total_balance: 340, + }, + ); + // pay total issuance to 1 at 2nd block + roll_blocks(3); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 5205 }); + // ~ set block author as 1 for 3 blocks this round + set_author(4, 1, 60); + // ~ set block author as 2 for 2 blocks this round + set_author(4, 2, 40); + roll_to_round_begin(6); + // pay 60% total issuance to 1 and 40% total issuance to 2 + assert_events_eq!( + Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 100 }, + Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 90 }, + Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 70 }, + Event::NewRound { + starting_block: 25, + round: 6, + selected_collators_number: 4, + total_balance: 340, + }, + ); + roll_blocks(3); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 3123 }); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 2, rewards: 2082 },); + // ~ each collator produces 1 block this round + set_author(6, 1, 20); + set_author(6, 2, 20); + set_author(6, 3, 20); + set_author(6, 4, 20); + roll_to_round_begin(8); + // pay 20% issuance for all collators + assert_events_eq!( + Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 100 }, + Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 90 }, + Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 80 }, + Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 70 }, + Event::NewRound { + starting_block: 35, + round: 8, + selected_collators_number: 4, + total_balance: 340, + }, + ); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 3, rewards: 1301 }); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 4, rewards: 1301 }); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 1301 }); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 2, rewards: 1301 }); + // check that distributing rewards clears awarded pts + assert!(ParachainStaking::awarded_pts(1, 1).is_zero()); + assert!(ParachainStaking::awarded_pts(4, 1).is_zero()); + assert!(ParachainStaking::awarded_pts(4, 2).is_zero()); + assert!(ParachainStaking::awarded_pts(6, 1).is_zero()); + assert!(ParachainStaking::awarded_pts(6, 2).is_zero()); + assert!(ParachainStaking::awarded_pts(6, 3).is_zero()); + assert!(ParachainStaking::awarded_pts(6, 4).is_zero()); + }); +} + +#[test] +fn multiple_delegations() { + 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_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) + .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .build() + .execute_with(|| { + roll_to_round_begin(2); + // chooses top TotalSelectedCandidates (5), in order + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 5, + total_balance: 140, + }, + ); + roll_blocks(1); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 2, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 3, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 4, 10, 10, 10)); + assert_events_eq!( + Event::Delegation { + delegator: 6, + locked_amount: 10, + candidate: 2, + delegator_position: DelegatorAdded::AddedToTop { new_total: 50 }, + auto_compound: Percent::zero(), + }, + Event::Delegation { + delegator: 6, + locked_amount: 10, + candidate: 3, + delegator_position: DelegatorAdded::AddedToTop { new_total: 30 }, + auto_compound: Percent::zero(), + }, + Event::Delegation { + delegator: 6, + locked_amount: 10, + candidate: 4, + delegator_position: DelegatorAdded::AddedToTop { new_total: 30 }, + auto_compound: Percent::zero(), + }, + ); + roll_to_round_begin(6); + roll_blocks(1); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(7), 2, 80, 10, 10)); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(10), 2, 10, 10, 10)); + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(2), 5)); + assert_events_eq!( + Event::Delegation { + delegator: 7, + locked_amount: 80, + candidate: 2, + delegator_position: DelegatorAdded::AddedToTop { new_total: 130 }, + auto_compound: Percent::zero(), + }, + Event::Delegation { + delegator: 10, + locked_amount: 10, + candidate: 2, + delegator_position: DelegatorAdded::AddedToBottom, + auto_compound: Percent::zero(), + }, + Event::CandidateScheduledExit { + exit_allowed_round: 6, + candidate: 2, + scheduled_exit: 8 + }, + ); + roll_to_round_begin(7); + assert_events_eq!( + Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 30 }, + Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 30 }, + Event::CollatorChosen { round: 7, collator_account: 5, total_exposed_amount: 10 }, + Event::NewRound { + starting_block: 30, + round: 7, + selected_collators_number: 4, + total_balance: 120, + }, + ); + // verify that delegations are removed after collator leaves, not before + assert_eq!(ParachainStaking::delegator_state(7).unwrap().total(), 90); + assert_eq!(ParachainStaking::delegator_state(7).unwrap().delegations.0.len(), 2usize); + assert_eq!(ParachainStaking::delegator_state(6).unwrap().total(), 40); + assert_eq!(ParachainStaking::delegator_state(6).unwrap().delegations.0.len(), 4usize); + assert_eq!(Balances::locks(6)[0].amount, 40); + assert_eq!(Balances::locks(7)[0].amount, 90); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&6), 60); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&7), 10); + roll_to_round_begin(8); + roll_blocks(1); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 2, 5)); + assert_events_eq!(Event::CandidateLeft { + ex_candidate: 2, + unlocked_amount: 140, + new_total_amt_locked: 120, + }); + assert_eq!(ParachainStaking::delegator_state(7).unwrap().total(), 10); + assert_eq!(ParachainStaking::delegator_state(6).unwrap().total(), 30); + assert_eq!(ParachainStaking::delegator_state(7).unwrap().delegations.0.len(), 1usize); + assert_eq!(ParachainStaking::delegator_state(6).unwrap().delegations.0.len(), 3usize); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&6), 70); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&7), 90); + }); +} + +#[test] +// The test verifies that the pending revoke request is removed by 2's exit so there is no dangling +// revoke request after 2 exits +fn execute_leave_candidate_removes_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 100), (2, 100), (3, 100), (4, 100)]) + .with_candidates(vec![(1, 20), (2, 20)]) + .with_delegations(vec![(3, 1, 10), (3, 2, 10), (4, 1, 10), (4, 2, 10)]) + .build() + .execute_with(|| { + // Verifies the revocation request is initially empty + assert!(!ParachainStaking::delegation_scheduled_requests(2) + .iter() + .any(|x| x.delegator == 3)); + + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(2), 2)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(3), 2)); + // Verifies the revocation request is present + assert!(ParachainStaking::delegation_scheduled_requests(2) + .iter() + .any(|x| x.delegator == 3)); + + roll_to(16); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 2, 2)); + // Verifies the revocation request is again empty + assert!(!ParachainStaking::delegation_scheduled_requests(2) + .iter() + .any(|x| x.delegator == 3)); + }); +} + +#[test] +fn payouts_follow_delegation_changes() { + ExtBuilder::default() + .with_balances(vec![ + (1, 100), + (2, 100), + (3, 100), + (4, 100), + (6, 100), + (7, 100), + (8, 100), + (9, 100), + (10, 100), + ]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .with_rewards_account(999, 100000) + .build() + .execute_with(|| { + roll_to_round_begin(2); + // chooses top TotalSelectedCandidates (5), in order + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 4, + total_balance: 130, + }, + ); + // ~ set block author as 1 for all blocks this round + set_author(2, 1, 100); + roll_to_round_begin(4); + // distribute total issuance to collator 1 and its delegators 6, 7, 19 + assert_events_eq!( + Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 15, + round: 4, + selected_collators_number: 4, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 2623 }, + Event::Rewarded { account: 6, rewards: 807 }, + Event::Rewarded { account: 7, rewards: 807 }, + Event::Rewarded { account: 10, rewards: 807 }, + ); + // ~ set block author as 1 for all blocks this round + set_author(3, 1, 100); + set_author(4, 1, 100); + set_author(5, 1, 100); + set_author(6, 1, 100); + + roll_blocks(1); + // 1. ensure delegators are paid for 2 rounds after they leave + assert_noop!( + ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(66), 1), + Error::::DelegatorDNE + ); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(6), 1,)); + assert_events_eq!(Event::DelegationRevocationScheduled { + round: 4, + delegator: 6, + candidate: 1, + scheduled_exit: 6, + }); + // fast forward to block in which delegator 6 exit executes + roll_to_round_begin(5); + assert_events_eq!( + Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 5, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 5, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 20, + round: 5, + selected_collators_number: 4, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 2623 }, + Event::Rewarded { account: 6, rewards: 807 }, + Event::Rewarded { account: 7, rewards: 807 }, + Event::Rewarded { account: 10, rewards: 807 }, + ); + // keep paying 6 (note: inflation is in terms of total issuance so that's why 1 is 21) + roll_to_round_begin(6); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(6), + 6, + 1, + )); + assert_events_eq!( + Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 25, + round: 6, + selected_collators_number: 4, + total_balance: 130, + }, + Event::DelegatorLeftCandidate { + delegator: 6, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 40, + }, + Event::DelegationRevoked { delegator: 6, candidate: 1, unstaked_amount: 10 }, + Event::DelegatorLeft { delegator: 6, unstaked_amount: 10 }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 2623 }, + Event::Rewarded { account: 6, rewards: 807 }, + Event::Rewarded { account: 7, rewards: 807 }, + Event::Rewarded { account: 10, rewards: 807 }, + ); + // 6 won't be paid for this round because they left already + set_author(7, 1, 100); + roll_to_round_begin(7); + // keep paying 6 + assert_events_eq!( + Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 7, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 30, + round: 7, + selected_collators_number: 4, + total_balance: 120, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 3027 }, + Event::Rewarded { account: 7, rewards: 1009 }, + Event::Rewarded { account: 10, rewards: 1009 }, + ); + roll_to_round_begin(8); + assert_events_eq!( + Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 35, + round: 8, + selected_collators_number: 4, + total_balance: 120, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 3027 }, + Event::Rewarded { account: 7, rewards: 1009 }, + Event::Rewarded { account: 10, rewards: 1009 }, + ); + set_author(8, 1, 100); + roll_to_round_begin(9); + // no more paying 6 + assert_events_eq!( + Event::CollatorChosen { round: 9, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 9, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 9, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 9, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 40, + round: 9, + selected_collators_number: 4, + total_balance: 120, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 3027 }, + Event::Rewarded { account: 7, rewards: 1009 }, + Event::Rewarded { account: 10, rewards: 1009 }, + ); + roll_blocks(1); + set_author(9, 1, 100); + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); + assert_events_eq!(Event::Delegation { + delegator: 8, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 50 }, + auto_compound: Percent::zero(), + }); + + roll_to_round_begin(10); + // new delegation is not rewarded yet + assert_events_eq!( + Event::CollatorChosen { round: 10, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 10, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 10, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 10, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 45, + round: 10, + selected_collators_number: 4, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 3027 }, + Event::Rewarded { account: 7, rewards: 1009 }, + Event::Rewarded { account: 10, rewards: 1009 }, + ); + set_author(10, 1, 100); + roll_to_round_begin(11); + // new delegation not rewarded yet + assert_events_eq!( + Event::CollatorChosen { round: 11, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 11, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 11, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 11, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 50, + round: 11, + selected_collators_number: 4, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 3027 }, + Event::Rewarded { account: 7, rewards: 1009 }, + Event::Rewarded { account: 10, rewards: 1009 }, + ); + roll_to_round_begin(12); + // new delegation is rewarded for first time + // 2 rounds after joining (`RewardPaymentDelay` = 2) + assert_events_eq!( + Event::CollatorChosen { round: 12, collator_account: 1, total_exposed_amount: 50 }, + Event::CollatorChosen { round: 12, collator_account: 2, total_exposed_amount: 40 }, + Event::CollatorChosen { round: 12, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 12, collator_account: 4, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 55, + round: 12, + selected_collators_number: 4, + total_balance: 130, + }, + ); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 2623 }, + Event::Rewarded { account: 7, rewards: 807 }, + Event::Rewarded { account: 10, rewards: 807 }, + Event::Rewarded { account: 8, rewards: 807 }, + ); + }); +} + +#[test] +fn bottom_delegations_are_empty_when_top_delegations_not_full() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 10), (3, 10), (4, 10), (5, 10)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // no top delegators => no bottom delegators + let top_delegations = ParachainStaking::top_delegations(1).unwrap(); + let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); + assert!(top_delegations.delegations.is_empty()); + assert!(bottom_delegations.delegations.is_empty()); + // 1 delegator => 1 top delegator, 0 bottom delegators + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 10, 10)); + let top_delegations = ParachainStaking::top_delegations(1).unwrap(); + let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); + assert_eq!(top_delegations.delegations.len(), 1usize); + assert!(bottom_delegations.delegations.is_empty()); + // 2 delegators => 2 top delegators, 0 bottom delegators + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(3), 1, 10, 10, 10)); + let top_delegations = ParachainStaking::top_delegations(1).unwrap(); + let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); + assert_eq!(top_delegations.delegations.len(), 2usize); + assert!(bottom_delegations.delegations.is_empty()); + // 3 delegators => 3 top delegators, 0 bottom delegators + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(4), 1, 10, 10, 10)); + let top_delegations = ParachainStaking::top_delegations(1).unwrap(); + let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); + assert_eq!(top_delegations.delegations.len(), 3usize); + assert!(bottom_delegations.delegations.is_empty()); + // 4 delegators => 4 top delegators, 0 bottom delegators + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(5), 1, 10, 10, 10)); + let top_delegations = ParachainStaking::top_delegations(1).unwrap(); + let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); + assert_eq!(top_delegations.delegations.len(), 4usize); + assert!(bottom_delegations.delegations.is_empty()); + }); +} + +#[test] +fn candidate_pool_updates_when_total_counted_changes() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (3, 19), + (4, 20), + (5, 21), + (6, 22), + (7, 15), + (8, 16), + (9, 17), + (10, 18), + ]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![ + (3, 1, 11), + (4, 1, 12), + (5, 1, 13), + (6, 1, 14), + (7, 1, 15), + (8, 1, 16), + (9, 1, 17), + (10, 1, 18), + ]) + .build() + .execute_with(|| { + fn is_candidate_pool_bond(account: u64, bond: u128) { + let pool = ParachainStaking::candidate_pool(); + for candidate in pool.0 { + if candidate.owner == account { + assert_eq!( + candidate.amount, bond, + "Candidate Bond {:?} is Not Equal to Expected: {:?}", + candidate.amount, bond + ); + } + } + } + // 15 + 16 + 17 + 18 + 20 = 86 (top 4 + self bond) + is_candidate_pool_bond(1, 86); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(3), 1, 8)); + // 3: 11 -> 19 => 3 is in top, bumps out 7 + // 16 + 17 + 18 + 19 + 20 = 90 (top 4 + self bond) + is_candidate_pool_bond(1, 90); + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(4), 1, 8)); + // 4: 12 -> 20 => 4 is in top, bumps out 8 + // 17 + 18 + 19 + 20 + 20 = 94 (top 4 + self bond) + is_candidate_pool_bond(1, 94); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(10), + 1, + 3 + )); + roll_to(30); + // 10: 18 -> 15 => 10 bumped to bottom, 8 bumped to top (- 18 + 16 = -2 for count) + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(10), + 10, + 1 + )); + // 16 + 17 + 19 + 20 + 20 = 92 (top 4 + self bond) + is_candidate_pool_bond(1, 92); + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(9), + 1, + 4 + )); + roll_to(40); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(9), + 9, + 1 + )); + // 15 + 16 + 19 + 20 + 20 = 90 (top 4 + self bond) + is_candidate_pool_bond(1, 90); + }); +} + +#[test] +fn only_top_collators_are_counted() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (3, 19), + (4, 20), + (5, 21), + (6, 22), + (7, 15), + (8, 16), + (9, 17), + (10, 18), + ]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![ + (3, 1, 11), + (4, 1, 12), + (5, 1, 13), + (6, 1, 14), + (7, 1, 15), + (8, 1, 16), + (9, 1, 17), + (10, 1, 18), + ]) + .build() + .execute_with(|| { + // sanity check that 3-10 are delegators immediately + for i in 3..11 { + assert!(ParachainStaking::is_delegator(&i)); + } + let collator_state = ParachainStaking::candidate_info(1).unwrap(); + // 15 + 16 + 17 + 18 + 20 = 86 (top 4 + self bond) + assert_eq!(collator_state.total_counted, 86); + // bump bottom to the top + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(3), 1, 8)); + assert_events_emitted!(Event::DelegationIncreased { + delegator: 3, + candidate: 1, + amount: 8, + in_top: true, + }); + let collator_state = ParachainStaking::candidate_info(1).unwrap(); + // 16 + 17 + 18 + 19 + 20 = 90 (top 4 + self bond) + assert_eq!(collator_state.total_counted, 90); + // bump bottom to the top + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(4), 1, 8)); + assert_events_emitted!(Event::DelegationIncreased { + delegator: 4, + candidate: 1, + amount: 8, + in_top: true, + }); + let collator_state = ParachainStaking::candidate_info(1).unwrap(); + // 17 + 18 + 19 + 20 + 20 = 94 (top 4 + self bond) + assert_eq!(collator_state.total_counted, 94); + // bump bottom to the top + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(5), 1, 8)); + assert_events_emitted!(Event::DelegationIncreased { + delegator: 5, + candidate: 1, + amount: 8, + in_top: true, + }); + let collator_state = ParachainStaking::candidate_info(1).unwrap(); + // 18 + 19 + 20 + 21 + 20 = 98 (top 4 + self bond) + assert_eq!(collator_state.total_counted, 98); + // bump bottom to the top + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(6), 1, 8)); + assert_events_emitted!(Event::DelegationIncreased { + delegator: 6, + candidate: 1, + amount: 8, + in_top: true, + }); + let collator_state = ParachainStaking::candidate_info(1).unwrap(); + // 19 + 20 + 21 + 22 + 20 = 102 (top 4 + self bond) + assert_eq!(collator_state.total_counted, 102); + }); +} + +#[test] +fn delegation_events_convey_correct_position() { + 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_candidates(vec![(1, 20), (2, 20)]) + .with_delegations(vec![(3, 1, 11), (4, 1, 12), (5, 1, 13), (6, 1, 14)]) + .build() + .execute_with(|| { + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 11 + 12 + 13 + 14 + 20 = 70 (top 4 + self bond) + assert_eq!(collator1_state.total_counted, 70); + // Top delegations are full, new highest delegation is made + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(7), 1, 15, 10, 10)); + assert_events_emitted!(Event::Delegation { + delegator: 7, + locked_amount: 15, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 74 }, + auto_compound: Percent::zero(), + }); + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 12 + 13 + 14 + 15 + 20 = 70 (top 4 + self bond) + assert_eq!(collator1_state.total_counted, 74); + // New delegation is added to the bottom + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); + assert_events_emitted!(Event::Delegation { + delegator: 8, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToBottom, + auto_compound: Percent::zero(), + }); + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 12 + 13 + 14 + 15 + 20 = 70 (top 4 + self bond) + assert_eq!(collator1_state.total_counted, 74); + // 8 increases delegation to the top + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(8), 1, 3)); + assert_events_emitted!(Event::DelegationIncreased { + delegator: 8, + candidate: 1, + amount: 3, + in_top: true, + }); + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 13 + 13 + 14 + 15 + 20 = 75 (top 4 + self bond) + assert_eq!(collator1_state.total_counted, 75); + // 3 increases delegation but stays in bottom + assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(3), 1, 1)); + assert_events_emitted!(Event::DelegationIncreased { + delegator: 3, + candidate: 1, + amount: 1, + in_top: false, + }); + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 13 + 13 + 14 + 15 + 20 = 75 (top 4 + self bond) + assert_eq!(collator1_state.total_counted, 75); + // 6 decreases delegation but stays in top + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(6), + 1, + 2 + )); + assert_events_emitted!(Event::DelegationDecreaseScheduled { + delegator: 6, + candidate: 1, + amount_to_decrease: 2, + execute_round: 3, + }); + roll_to(30); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(6), + 6, + 1 + )); + assert_events_emitted!(Event::DelegationDecreased { + delegator: 6, + candidate: 1, + amount: 2, + in_top: true, + }); + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 12 + 13 + 13 + 15 + 20 = 73 (top 4 + self bond)ƒ + assert_eq!(collator1_state.total_counted, 73); + // 6 decreases delegation and is bumped to bottom + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(6), + 1, + 1 + )); + assert_events_emitted!(Event::DelegationDecreaseScheduled { + delegator: 6, + candidate: 1, + amount_to_decrease: 1, + execute_round: 9, + }); + roll_to(40); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(6), + 6, + 1 + )); + assert_events_emitted!(Event::DelegationDecreased { + delegator: 6, + candidate: 1, + amount: 1, + in_top: false, + }); + let collator1_state = ParachainStaking::candidate_info(1).unwrap(); + // 12 + 13 + 13 + 15 + 20 = 73 (top 4 + self bond) + assert_eq!(collator1_state.total_counted, 73); + }); +} + +#[test] +fn no_rewards_paid_until_after_reward_payment_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + roll_to_round_begin(2); + // payouts for round 1 + set_author(1, 1, 1); + set_author(1, 2, 1); + set_author(1, 2, 1); + set_author(1, 3, 1); + set_author(1, 3, 1); + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 3, + total_balance: 60, + }, + ); + + roll_to_round_begin(3); + assert_events_eq!( + Event::CollatorChosen { round: 3, collator_account: 1, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 3, collator_account: 2, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 3, collator_account: 3, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 10, + round: 3, + selected_collators_number: 3, + total_balance: 60, + }, + ); + + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 3, rewards: 3 }); + + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 2 }); + + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 2, rewards: 3 }); + + // there should be no more payments in this round... + let num_blocks_rolled = roll_to_round_end(3); + assert_no_events!(); + assert_eq!(num_blocks_rolled, 1); + }); +} + +#[test] +fn deferred_payment_storage_items_are_cleaned_up() { + use crate::*; + + // this test sets up two collators, gives them points in round one, and focuses on the + // storage over the next several blocks to show that it is properly cleaned up + + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20)]) + .with_candidates(vec![(1, 20), (2, 20)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + set_author(1, 1, 1); + set_author(1, 2, 1); + + // reflects genesis? + assert!(>::contains_key(1, 1)); + assert!(>::contains_key(1, 2)); + + roll_to_round_begin(2); + assert_events_eq!( + Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 5, + round: 2, + selected_collators_number: 2, + total_balance: 40, + }, + ); + + // we should have AtStake snapshots as soon as we start a round... + assert!(>::contains_key(2, 1)); + assert!(>::contains_key(2, 2)); + // ...and it should persist until the round is fully paid out + assert!(>::contains_key(1, 1)); + assert!(>::contains_key(1, 2)); + + assert!( + !>::contains_key(1), + "DelayedPayouts shouldn't be populated until after RewardPaymentDelay" + ); + assert!( + >::contains_key(1), + "Points should be populated during current round" + ); + assert!( + >::contains_key(1), + "Staked should be populated when round changes" + ); + + assert!( + !>::contains_key(2), + "Points should not be populated until author noted" + ); + assert!( + >::contains_key(2), + "Staked should be populated when round changes" + ); + + // first payout occurs in round 3 + roll_to_round_begin(3); + assert_events_eq!( + Event::CollatorChosen { round: 3, collator_account: 1, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 3, collator_account: 2, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 10, + round: 3, + selected_collators_number: 2, + total_balance: 40, + }, + ); + + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 3 },); + + // payouts should exist for past rounds that haven't been paid out yet.. + assert!(>::contains_key(3, 1)); + assert!(>::contains_key(3, 2)); + assert!(>::contains_key(2, 1)); + assert!(>::contains_key(2, 2)); + + assert!( + >::contains_key(1), + "DelayedPayouts should be populated after RewardPaymentDelay" + ); + assert!(>::contains_key(1)); + assert!( + !>::contains_key(1), + "Staked should be cleaned up after round change" + ); + + assert!(!>::contains_key(2)); + assert!(!>::contains_key(2), "We never rewarded points for round 2"); + assert!(>::contains_key(2)); + + assert!(!>::contains_key(3)); + assert!(!>::contains_key(3), "We never awarded points for round 3"); + assert!(>::contains_key(3)); + + // collator 1 has been paid in this last block and associated storage cleaned up + assert!(!>::contains_key(1, 1)); + assert!(!>::contains_key(1, 1)); + + // but collator 2 hasn't been paid + assert!(>::contains_key(1, 2)); + assert!(>::contains_key(1, 2)); + + // second payout occurs in next block + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: 2, rewards: 3 },); + + roll_to_round_begin(4); + assert_events_eq!( + Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 20 }, + Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 20 }, + Event::NewRound { + starting_block: 15, + round: 4, + selected_collators_number: 2, + total_balance: 40, + }, + ); + + // collators have both been paid and storage fully cleaned up for round 1 + assert!(!>::contains_key(1, 2)); + assert!(!>::contains_key(1, 2)); + assert!(!>::contains_key(1)); + assert!(!>::contains_key(1)); // points should be cleaned up + assert!(!>::contains_key(1)); + + roll_to_round_end(4); + + // no more events expected + assert_no_events!(); + }); +} + +#[test] +fn deferred_payment_and_at_stake_storage_items_cleaned_up_for_candidates_not_producing_blocks() { + use crate::*; + + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 20), (2, 20), (3, 20)]) + .build() + .execute_with(|| { + // candidate 3 will not produce blocks + set_author(1, 1, 1); + set_author(1, 2, 1); + + // reflects genesis? + assert!(>::contains_key(1, 1)); + assert!(>::contains_key(1, 2)); + + roll_to_round_begin(2); + assert!(>::contains_key(1, 1)); + assert!(>::contains_key(1, 2)); + assert!(>::contains_key(1, 3)); + assert!(>::contains_key(1, 1)); + assert!(>::contains_key(1, 2)); + assert!(!>::contains_key(1, 3)); + assert!(>::contains_key(1)); + assert!(>::contains_key(1)); + roll_to_round_begin(3); + assert!(>::contains_key(1)); + + // all storage items must be cleaned up + roll_to_round_begin(4); + assert!(!>::contains_key(1, 1)); + assert!(!>::contains_key(1, 2)); + assert!(!>::contains_key(1, 3)); + assert!(!>::contains_key(1, 1)); + assert!(!>::contains_key(1, 2)); + assert!(!>::contains_key(1, 3)); + assert!(!>::contains_key(1)); + assert!(!>::contains_key(1)); + assert!(!>::contains_key(1)); + }); +} + +#[test] +fn deferred_payment_steady_state_event_flow() { + use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; + + // this test "flows" through a number of rounds, asserting that certain things do/don't happen + // once the staking pallet is in a "steady state" (specifically, once we are past the first few + // rounds to clear RewardPaymentDelay) + + ExtBuilder::default() + .with_balances(vec![ + // collators + (1, 200), + (2, 200), + (3, 200), + (4, 200), + // delegators + (11, 200), + (22, 200), + (33, 200), + (44, 200), + // burn account, see `reset_issuance()` + (111, 1000), + ]) + .with_candidates(vec![(1, 200), (2, 200), (3, 200), (4, 200)]) + .with_delegations(vec![ + // delegator 11 delegates 100 to 1 and 2 + (11, 1, 100), + (11, 2, 100), + // delegator 22 delegates 100 to 2 and 3 + (22, 2, 100), + (22, 3, 100), + // delegator 33 delegates 100 to 3 and 4 + (33, 3, 100), + (33, 4, 100), + // delegator 44 delegates 100 to 4 and 1 + (44, 4, 100), + (44, 1, 100), + ]) + .with_rewards_account(999, 100000) + .build() + .execute_with(|| { + // convenience to set the round points consistently + let set_round_points = |round: BlockNumber| { + set_author(round as BlockNumber, 1, 1); + set_author(round as BlockNumber, 2, 1); + set_author(round as BlockNumber, 3, 1); + set_author(round as BlockNumber, 4, 1); + }; + + // grab initial issuance -- we will reset it before round issuance is calculated so that + // it is consistent every round + let initial_issuance = Balances::total_issuance(); + let reset_issuance = || { + let new_issuance = Balances::total_issuance(); + let diff = new_issuance - initial_issuance; + let burned = Balances::burn(diff); + Balances::settle( + &111, + burned, + WithdrawReasons::FEE, + ExistenceRequirement::AllowDeath, + ) + .expect("Account can absorb burn"); + }; + + // fn to roll through the first RewardPaymentDelay rounds. returns new round index + let roll_through_initial_rounds = |mut round: BlockNumber| -> BlockNumber { + while round < crate::mock::RewardPaymentDelay::get() + 1 { + set_round_points(round); + + roll_to_round_end(round); + round += 1; + } + + reset_issuance(); + + round + }; + + // roll through a "steady state" round and make all of our assertions + // returns new round index + let roll_through_steady_state_round = |round: BlockNumber| -> BlockNumber { + let num_rounds_rolled = roll_to_round_begin(round); + assert!(num_rounds_rolled <= 1, "expected to be at round begin already"); + + assert_events_eq!( + Event::CollatorChosen { round, collator_account: 1, total_exposed_amount: 400 }, + Event::CollatorChosen { round, collator_account: 2, total_exposed_amount: 400 }, + Event::CollatorChosen { round, collator_account: 3, total_exposed_amount: 400 }, + Event::CollatorChosen { round, collator_account: 4, total_exposed_amount: 400 }, + Event::NewRound { + starting_block: (round as u64 - 1) * 5, + round, + selected_collators_number: 4, + total_balance: 1600, + }, + ); + + set_round_points(round); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 3, rewards: 769 }, + Event::Rewarded { account: 22, rewards: 256 }, + Event::Rewarded { account: 33, rewards: 256 }, + ); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 4, rewards: 769 }, + Event::Rewarded { account: 33, rewards: 256 }, + Event::Rewarded { account: 44, rewards: 256 }, + ); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 769 }, + Event::Rewarded { account: 11, rewards: 256 }, + Event::Rewarded { account: 44, rewards: 256 }, + ); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 2, rewards: 769 }, + Event::Rewarded { account: 11, rewards: 256 }, + Event::Rewarded { account: 22, rewards: 256 }, + ); + + roll_blocks(1); + // Since we defer first deferred staking payout, this test have the maximum amout of + // supported collators. This eman that the next round is trigerred one block after + // the last reward. + //assert_no_events!(); + + let num_rounds_rolled = roll_to_round_end(round); + assert_eq!(num_rounds_rolled, 0, "expected to be at round end already"); + + reset_issuance(); + + round + 1 + }; + + let mut round = 1; + round = roll_through_initial_rounds(round); // we should be at RewardPaymentDelay + for _ in 1..2 { + round = roll_through_steady_state_round(round); + } + }); +} + +#[test] +fn delegation_kicked_from_bottom_removes_pending_request() { + ExtBuilder::default() + .with_balances(vec![ + (1, 30), + (2, 29), + (3, 20), + (4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20), + (9, 20), + (10, 20), + (11, 30), + ]) + .with_candidates(vec![(1, 30), (11, 30)]) + .with_delegations(vec![ + (2, 1, 19), + (2, 11, 10), // second delegation so not left after first is kicked + (3, 1, 20), + (4, 1, 20), + (5, 1, 20), + (6, 1, 20), + (7, 1, 20), + (8, 1, 20), + (9, 1, 20), + ]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + // 10 delegates to full 1 => kicks lowest delegation (2, 19) + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(10), 1, 20, 8, 0)); + // check the event + assert_events_emitted!(Event::DelegationKicked { + delegator: 2, + candidate: 1, + unstaked_amount: 19, + }); + // ensure request DNE + assert!(!ParachainStaking::delegation_scheduled_requests(1) + .iter() + .any(|x| x.delegator == 2)); + }); +} + +#[test] +fn no_selected_candidates_defaults_to_last_round_collators() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 30), (3, 30), (4, 30), (5, 30)]) + .with_candidates(vec![(1, 30), (2, 30), (3, 30), (4, 30), (5, 30)]) + .build() + .execute_with(|| { + roll_to_round_begin(1); + // schedule to leave + for i in 1..6 { + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(i), + 5 + )); + } + let old_round = ParachainStaking::round().current; + let old_selected_candidates = ParachainStaking::selected_candidates(); + let mut old_at_stake_snapshots = Vec::new(); + for account in old_selected_candidates.clone() { + old_at_stake_snapshots.push(>::get(old_round, account)); + } + roll_to_round_begin(3); + // execute leave + for i in 1..6 { + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(i), + i, + 0, + )); + } + // next round + roll_to_round_begin(4); + let new_round = ParachainStaking::round().current; + // check AtStake matches previous + let new_selected_candidates = ParachainStaking::selected_candidates(); + assert_eq!(old_selected_candidates, new_selected_candidates); + for (index, account) in new_selected_candidates.into_iter().enumerate() { + assert_eq!(old_at_stake_snapshots[index], >::get(new_round, account)); + } + }); +} + +#[test] +fn test_delegator_scheduled_for_revoke_is_rewarded_for_previous_rounds_but_not_for_future() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + // preset rewards for rounds 1, 2 and 3 + (1..=3).for_each(|round| set_author(round, 1, 1)); + + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_events_eq!(Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }); + let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); + assert_eq!( + 1, collator.delegation_count, + "collator's delegator count was reduced unexpectedly" + ); + assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + + roll_to_round_begin(3); + assert_events_emitted_match!(Event::NewRound { round: 3, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 7 }, + Event::Rewarded { account: 2, rewards: 3 }, + ); + + roll_to_round_begin(4); + assert_events_emitted_match!(Event::NewRound { round: 4, .. }); + roll_blocks(3); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 10 },); + let collator_snapshot = + ParachainStaking::at_stake(ParachainStaking::round().current, 1) + .unwrap_or_default(); + assert_eq!( + 1, + collator_snapshot.delegations.len(), + "collator snapshot's delegator count was reduced unexpectedly" + ); + assert_eq!( + 20, collator_snapshot.total, + "collator snapshot's total was reduced unexpectedly", + ); + }); +} + +#[test] +fn test_delegator_scheduled_for_revoke_is_rewarded_when_request_cancelled() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + // preset rewards for rounds 2, 3 and 4 + (2..=4).for_each(|round| set_author(round, 1, 1)); + + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_events_eq!(Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }); + let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); + assert_eq!( + 1, collator.delegation_count, + "collator's delegator count was reduced unexpectedly" + ); + assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + + roll_to_round_begin(2); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + + roll_to_round_begin(4); + assert_events_emitted_match!(Event::NewRound { round: 4, .. }); + roll_blocks(3); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 9 },); + let collator_snapshot = + ParachainStaking::at_stake(ParachainStaking::round().current, 1) + .unwrap_or_default(); + assert_eq!( + 1, + collator_snapshot.delegations.len(), + "collator snapshot's delegator count was reduced unexpectedly" + ); + assert_eq!( + 30, collator_snapshot.total, + "collator snapshot's total was reduced unexpectedly", + ); + + roll_to_round_begin(5); + assert_events_emitted_match!(Event::NewRound { round: 5, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 7 }, + Event::Rewarded { account: 2, rewards: 2 }, + ); + }); +} + +#[test] +fn test_delegator_scheduled_for_bond_decrease_is_rewarded_for_previous_rounds_but_less_for_future() +{ + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 20), (2, 3, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + // preset rewards for rounds 1, 2 and 3 + (1..=3).for_each(|round| set_author(round, 1, 1)); + + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 10, + )); + assert_events_eq!(Event::DelegationDecreaseScheduled { + execute_round: 3, + delegator: 2, + candidate: 1, + amount_to_decrease: 10, + }); + let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); + assert_eq!( + 1, collator.delegation_count, + "collator's delegator count was reduced unexpectedly" + ); + assert_eq!(40, collator.total_counted, "collator's total was reduced unexpectedly"); + + roll_to_round_begin(3); + assert_events_emitted_match!(Event::NewRound { round: 3, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 6 }, + Event::Rewarded { account: 2, rewards: 4 }, + ); + + roll_to_round_begin(4); + assert_events_emitted_match!(Event::NewRound { round: 4, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 7 }, + Event::Rewarded { account: 2, rewards: 3 }, + ); + let collator_snapshot = + ParachainStaking::at_stake(ParachainStaking::round().current, 1) + .unwrap_or_default(); + assert_eq!( + 1, + collator_snapshot.delegations.len(), + "collator snapshot's delegator count was reduced unexpectedly" + ); + assert_eq!( + 30, collator_snapshot.total, + "collator snapshot's total was reduced unexpectedly", + ); + }); +} + +#[test] +fn test_delegator_scheduled_for_bond_decrease_is_rewarded_when_request_cancelled() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 20), (2, 3, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + // preset rewards for rounds 2, 3 and 4 + (2..=4).for_each(|round| set_author(round, 1, 1)); + + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 10, + )); + assert_events_eq!(Event::DelegationDecreaseScheduled { + execute_round: 3, + delegator: 2, + candidate: 1, + amount_to_decrease: 10, + }); + let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); + assert_eq!( + 1, collator.delegation_count, + "collator's delegator count was reduced unexpectedly" + ); + assert_eq!(40, collator.total_counted, "collator's total was reduced unexpectedly"); + + roll_to_round_begin(2); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + + roll_to_round_begin(4); + assert_events_emitted_match!(Event::NewRound { round: 4, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 7 }, + Event::Rewarded { account: 2, rewards: 3 }, + ); + let collator_snapshot = + ParachainStaking::at_stake(ParachainStaking::round().current, 1) + .unwrap_or_default(); + assert_eq!( + 1, + collator_snapshot.delegations.len(), + "collator snapshot's delegator count was reduced unexpectedly" + ); + assert_eq!( + 40, collator_snapshot.total, + "collator snapshot's total was reduced unexpectedly", + ); + + roll_to_round_begin(5); + assert_events_emitted_match!(Event::NewRound { round: 5, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 6 }, + Event::Rewarded { account: 2, rewards: 4 }, + ); + }); +} + +#[test] +fn test_delegator_scheduled_for_leave_is_rewarded_for_previous_rounds_but_not_for_future() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + // preset rewards for rounds 1, 2 and 3 + (1..=3).for_each(|round| set_author(round, 1, 1)); + + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3,)); + assert_events_eq!( + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }, + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 3, + scheduled_exit: 3, + }, + ); + let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); + assert_eq!( + 1, collator.delegation_count, + "collator's delegator count was reduced unexpectedly" + ); + assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + + roll_to_round_begin(3); + assert_events_emitted_match!(Event::NewRound { round: 3, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 7 }, + Event::Rewarded { account: 2, rewards: 3 }, + ); + + roll_to_round_begin(4); + assert_events_emitted_match!(Event::NewRound { round: 4, .. }); + roll_blocks(3); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 10 },); + let collator_snapshot = + ParachainStaking::at_stake(ParachainStaking::round().current, 1) + .unwrap_or_default(); + assert_eq!( + 1, + collator_snapshot.delegations.len(), + "collator snapshot's delegator count was reduced unexpectedly" + ); + assert_eq!( + 20, collator_snapshot.total, + "collator snapshot's total was reduced unexpectedly", + ); + }); +} + +#[test] +fn test_delegator_scheduled_for_leave_is_rewarded_when_request_cancelled() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + // preset rewards for rounds 2, 3 and 4 + (2..=4).for_each(|round| set_author(round, 1, 1)); + + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3,)); + assert_events_eq!( + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }, + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 3, + scheduled_exit: 3, + }, + ); + let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); + assert_eq!( + 1, collator.delegation_count, + "collator's delegator count was reduced unexpectedly" + ); + assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + + roll_to_round_begin(2); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 3,)); + + roll_to_round_begin(4); + assert_events_emitted_match!(Event::NewRound { round: 4, .. }); + roll_blocks(3); + assert_events_eq!(Event::Rewarded { account: 1, rewards: 10 },); + let collator_snapshot = + ParachainStaking::at_stake(ParachainStaking::round().current, 1) + .unwrap_or_default(); + assert_eq!( + 1, + collator_snapshot.delegations.len(), + "collator snapshot's delegator count was reduced unexpectedly" + ); + assert_eq!( + 30, collator_snapshot.total, + "collator snapshot's total was reduced unexpectedly", + ); + + roll_to_round_begin(5); + assert_events_emitted_match!(Event::NewRound { round: 5, .. }); + roll_blocks(3); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 7 }, + Event::Rewarded { account: 2, rewards: 3 }, + ); + }); +} + +#[test] +fn test_delegation_request_exists_returns_false_when_nothing_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert!(!ParachainStaking::delegation_request_exists(&1, &2)); + }); +} + +#[test] +fn test_delegation_request_exists_returns_true_when_decrease_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::insert( + 1, + BoundedVec::try_from(vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Decrease(5), + }]) + .expect("must succeed"), + ); + assert!(ParachainStaking::delegation_request_exists(&1, &2)); + }); +} + +#[test] +fn test_delegation_request_exists_returns_true_when_revoke_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::insert( + 1, + BoundedVec::try_from(vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Revoke(5), + }]) + .expect("must succeed"), + ); + assert!(ParachainStaking::delegation_request_exists(&1, &2)); + }); +} + +#[test] +fn test_delegation_request_revoke_exists_returns_false_when_nothing_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert!(!ParachainStaking::delegation_request_revoke_exists(&1, &2)); + }); +} + +#[test] +fn test_delegation_request_revoke_exists_returns_false_when_decrease_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::insert( + 1, + BoundedVec::try_from(vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Decrease(5), + }]) + .expect("must succeed"), + ); + assert!(!ParachainStaking::delegation_request_revoke_exists(&1, &2)); + }); +} + +#[test] +fn test_delegation_request_revoke_exists_returns_true_when_revoke_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::insert( + 1, + BoundedVec::try_from(vec![ScheduledRequest { + delegator: 2, + when_executable: 3, + action: DelegationAction::Revoke(5), + }]) + .expect("must succeed"), + ); + assert!(ParachainStaking::delegation_request_revoke_exists(&1, &2)); + }); +} + +#[test] +fn test_hotfix_remove_delegation_requests_exited_candidates_cleans_up() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // invalid state + >::insert(2, BoundedVec::default()); + >::insert(3, BoundedVec::default()); + assert_ok!(ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( + RuntimeOrigin::signed(1), + vec![2, 3, 4] // 4 does not exist, but is OK for idempotency + )); + + assert!(!>::contains_key(2)); + assert!(!>::contains_key(3)); + }); +} + +#[test] +fn test_hotfix_remove_delegation_requests_exited_candidates_cleans_up_only_specified_keys() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // invalid state + >::insert(2, BoundedVec::default()); + >::insert(3, BoundedVec::default()); + assert_ok!(ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( + RuntimeOrigin::signed(1), + vec![2] + )); + + assert!(!>::contains_key(2)); + assert!(>::contains_key(3)); + }); +} + +#[test] +fn test_hotfix_remove_delegation_requests_exited_candidates_errors_when_requests_not_empty() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // invalid state + >::insert(2, BoundedVec::default()); + >::insert( + 3, + BoundedVec::try_from(vec![ScheduledRequest { + delegator: 10, + when_executable: 1, + action: DelegationAction::Revoke(10), + }]) + .expect("must succeed"), + ); + + assert_noop!( + ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( + RuntimeOrigin::signed(1), + vec![2, 3] + ), + >::CandidateNotLeaving, + ); + }); +} + +#[test] +fn test_hotfix_remove_delegation_requests_exited_candidates_errors_when_candidate_not_exited() { + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // invalid state + >::insert(1, BoundedVec::default()); + assert_noop!( + ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( + RuntimeOrigin::signed(1), + vec![1] + ), + >::CandidateNotLeaving, + ); + }); +} + +#[test] +fn locking_zero_amount_removes_lock() { + use frame_support::traits::{LockableCurrency, WithdrawReasons}; + + // this test demonstrates the behavior of pallet Balance's `LockableCurrency` implementation of + // `set_locks()` when an amount of 0 is provided: any previous lock is removed + + ExtBuilder::default().with_balances(vec![(1, 100)]).build().execute_with(|| { + assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), None); + + Balances::set_lock(DELEGATOR_LOCK_ID, &1, 1, WithdrawReasons::all()); + assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), Some(1)); + + Balances::set_lock(DELEGATOR_LOCK_ID, &1, 0, WithdrawReasons::all()); + // Note that we tried to call `set_lock(0)` and the previous lock gets removed + assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), None); + }); +} + +#[test] +fn revoke_last_removes_lock() { + ExtBuilder::default() + .with_balances(vec![(1, 100), (2, 100), (3, 100)]) + .with_candidates(vec![(1, 25), (2, 25)]) + .with_delegations(vec![(3, 1, 30), (3, 2, 25)]) + .build() + .execute_with(|| { + assert_eq!(crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), Some(55)); + + // schedule and remove one... + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(3), 1)); + roll_to_round_begin(3); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(3), + 3, + 1 + )); + assert_eq!(crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), Some(25)); + + // schedule and remove the other... + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(3), 2)); + roll_to_round_begin(5); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(3), + 3, + 2 + )); + assert_eq!(crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), None); + }); +} + +#[test] +fn test_set_auto_compound_fails_if_invalid_delegation_hint() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + let candidate_auto_compounding_delegation_count_hint = 0; + let delegation_hint = 0; // is however, 1 + + assert_noop!( + ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + candidate_auto_compounding_delegation_count_hint, + delegation_hint, + ), + >::TooLowDelegationCountToAutoCompound, + ); + }); +} + +#[test] +fn test_set_auto_compound_fails_if_invalid_candidate_auto_compounding_hint() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::new( + vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(10) }] + .try_into() + .expect("must succeed"), + ) + .set_storage(&1); + let candidate_auto_compounding_delegation_count_hint = 0; // is however, 1 + let delegation_hint = 1; + + assert_noop!( + ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + candidate_auto_compounding_delegation_count_hint, + delegation_hint, + ), + >::TooLowCandidateAutoCompoundingDelegationCountToAutoCompound, + ); + }); +} + +#[test] +fn test_set_auto_compound_inserts_if_not_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 0, + 1, + )); + assert_events_emitted!(Event::AutoCompoundSet { + candidate: 1, + delegator: 2, + value: Percent::from_percent(50), + }); + assert_eq!( + vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(50) }], + ParachainStaking::auto_compounding_delegations(1).into_inner(), + ); + }); +} + +#[test] +fn test_set_auto_compound_updates_if_existing() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::new( + vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(10) }] + .try_into() + .expect("must succeed"), + ) + .set_storage(&1); + + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 1, + 1, + )); + assert_events_emitted!(Event::AutoCompoundSet { + candidate: 1, + delegator: 2, + value: Percent::from_percent(50), + }); + assert_eq!( + vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(50) }], + ParachainStaking::auto_compounding_delegations(1).into_inner(), + ); + }); +} + +#[test] +fn test_set_auto_compound_removes_if_auto_compound_zero_percent() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + >::new( + vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(10) }] + .try_into() + .expect("must succeed"), + ) + .set_storage(&1); + + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::zero(), + 1, + 1, + )); + assert_events_emitted!(Event::AutoCompoundSet { + candidate: 1, + delegator: 2, + value: Percent::zero(), + }); + assert_eq!(0, ParachainStaking::auto_compounding_delegations(1).len(),); + }); +} + +#[test] +fn test_execute_revoke_delegation_removes_auto_compounding_from_state_for_delegation_revoke() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 30), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 0, + 2, + )); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 3, + Percent::from_percent(50), + 0, + 2, + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1 + )); + assert!( + !ParachainStaking::auto_compounding_delegations(1) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was not removed" + ); + assert!( + ParachainStaking::auto_compounding_delegations(3) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was erroneously removed" + ); + }); +} + +#[test] +fn test_execute_leave_delegators_removes_auto_compounding_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 0, + 2, + )); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 3, + Percent::from_percent(50), + 0, + 2, + )); + + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3,)); + roll_to(10); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 1, + )); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), + 2, + 3, + )); + + assert!( + !ParachainStaking::auto_compounding_delegations(1) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was not removed" + ); + assert!( + !ParachainStaking::auto_compounding_delegations(3) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was not removed" + ); + }); +} + +#[test] +fn test_execute_leave_candidates_removes_auto_compounding_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 30), (3, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 0, + 2, + )); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 3, + Percent::from_percent(50), + 0, + 2, + )); + + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 2)); + roll_to(10); + assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 1,)); + + assert!( + !ParachainStaking::auto_compounding_delegations(1) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was not removed" + ); + assert!( + ParachainStaking::auto_compounding_delegations(3) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was erroneously removed" + ); + }); +} + +#[test] +fn test_delegation_kicked_from_bottom_delegation_removes_auto_compounding_state() { + ExtBuilder::default() + .with_balances(vec![ + (1, 30), + (2, 29), + (3, 20), + (4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20), + (9, 20), + (10, 20), + (11, 30), + ]) + .with_candidates(vec![(1, 30), (11, 30)]) + .with_delegations(vec![ + (2, 11, 10), // extra delegation to avoid leaving the delegator set + (2, 1, 19), + (3, 1, 20), + (4, 1, 20), + (5, 1, 20), + (6, 1, 20), + (7, 1, 20), + (8, 1, 20), + (9, 1, 20), + ]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 0, + 2, + )); + + // kicks lowest delegation (2, 19) + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(10), 1, 20, 8, 0)); + + assert!( + !ParachainStaking::auto_compounding_delegations(1) + .iter() + .any(|x| x.delegator == 2), + "delegation auto-compound config was not removed" + ); + }); +} + +#[test] +fn test_rewards_do_not_auto_compound_on_payment_if_delegation_scheduled_revoke_exists() { + ExtBuilder::default() + .with_balances(vec![(1, 100), (2, 200), (3, 200)]) + .with_candidates(vec![(1, 100)]) + .with_delegations(vec![(2, 1, 200), (3, 1, 200)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + (2..=5).for_each(|round| set_author(round, 1, 1)); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(50), + 0, + 1, + )); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(3), + 1, + Percent::from_percent(50), + 1, + 1, + )); + roll_to_round_begin(3); + + // schedule revoke for delegator 2; no rewards should be compounded + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + roll_to_round_begin(4); + + assert_events_eq!( + Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 500 }, + Event::NewRound { + starting_block: 15, + round: 4, + selected_collators_number: 1, + total_balance: 500, + }, + ); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 11 }, + // no compound since revoke request exists + Event::Rewarded { account: 2, rewards: 10 }, + // 50% + Event::Rewarded { account: 3, rewards: 10 }, + Event::Compounded { candidate: 1, delegator: 3, amount: 5 }, + ); + }); +} + +#[test] +fn test_rewards_auto_compound_on_payment_as_per_auto_compound_config() { + ExtBuilder::default() + .with_balances(vec![(1, 100), (2, 200), (3, 200), (4, 200), (5, 200)]) + .with_candidates(vec![(1, 100)]) + .with_delegations(vec![(2, 1, 200), (3, 1, 200), (4, 1, 200), (5, 1, 200)]) + .with_rewards_account(999, 100) + .build() + .execute_with(|| { + (2..=6).for_each(|round| set_author(round, 1, 1)); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(2), + 1, + Percent::from_percent(0), + 0, + 1, + )); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(3), + 1, + Percent::from_percent(50), + 1, + 1, + )); + assert_ok!(ParachainStaking::set_auto_compound( + RuntimeOrigin::signed(4), + 1, + Percent::from_percent(100), + 2, + 1, + )); + roll_to_round_begin(4); + + assert_events_eq!( + Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 900 }, + Event::NewRound { + starting_block: 15, + round: 4, + selected_collators_number: 1, + total_balance: 900, + }, + ); + + roll_blocks(1); + assert_events_eq!( + Event::Rewarded { account: 1, rewards: 14 }, + // 0% + Event::Rewarded { account: 2, rewards: 9 }, + // 50% + Event::Rewarded { account: 3, rewards: 9 }, + Event::Compounded { candidate: 1, delegator: 3, amount: 5 }, + // 100% + Event::Rewarded { account: 4, rewards: 9 }, + Event::Compounded { candidate: 1, delegator: 4, amount: 9 }, + // no-config + Event::Rewarded { account: 5, rewards: 9 }, + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_fails_if_invalid_delegation_hint() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 3, 10)]) + .build() + .execute_with(|| { + let candidate_delegation_count_hint = 0; + let candidate_auto_compounding_delegation_count_hint = 0; + let delegation_hint = 0; // is however, 1 + + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + candidate_delegation_count_hint, + candidate_auto_compounding_delegation_count_hint, + delegation_hint, + ), + >::TooLowDelegationCountToDelegate, + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_fails_if_invalid_candidate_delegation_count_hint() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25), (3, 30)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(3, 1, 10)]) + .build() + .execute_with(|| { + let candidate_delegation_count_hint = 0; // is however, 1 + let candidate_auto_compounding_delegation_count_hint = 0; + let delegation_hint = 0; + + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + candidate_delegation_count_hint, + candidate_auto_compounding_delegation_count_hint, + delegation_hint, + ), + >::TooLowCandidateDelegationCountToDelegate, + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_fails_if_invalid_candidate_auto_compounding_delegations_hint() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25), (3, 30)]) + .with_candidates(vec![(1, 30)]) + .with_auto_compounding_delegations(vec![(3, 1, 10, Percent::from_percent(10))]) + .build() + .execute_with(|| { + let candidate_delegation_count_hint = 1; + let candidate_auto_compounding_delegation_count_hint = 0; // is however, 1 + let delegation_hint = 0; + + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + candidate_delegation_count_hint, + candidate_auto_compounding_delegation_count_hint, + delegation_hint, + ), + >::TooLowCandidateAutoCompoundingDelegationCountToDelegate, + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_sets_auto_compound_config() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 25)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + 0, + 0, + 0, + )); + assert_events_emitted!(Event::Delegation { + delegator: 2, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 40 }, + auto_compound: Percent::from_percent(50), + }); + assert_eq!( + vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(50) }], + ParachainStaking::auto_compounding_delegations(1).into_inner(), + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_skips_storage_but_emits_event_for_zero_auto_compound() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::zero(), + 0, + 0, + 0, + )); + assert_eq!(0, ParachainStaking::auto_compounding_delegations(1).len(),); + assert_events_eq!(Event::Delegation { + delegator: 2, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 40 }, + auto_compound: Percent::zero(), + }); + }); +} + +#[test] +fn test_delegate_with_auto_compound_reserves_balance() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + 0, + 0, + 0, + )); + assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + }); +} + +#[test] +fn test_delegate_with_auto_compound_updates_delegator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + assert!(ParachainStaking::delegator_state(2).is_none()); + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + 0, + 0, + 0 + )); + let delegator_state = + ParachainStaking::delegator_state(2).expect("just delegated => exists"); + assert_eq!(delegator_state.total(), 10); + assert_eq!(delegator_state.delegations.0[0].owner, 1); + assert_eq!(delegator_state.delegations.0[0].amount, 10); + }); +} + +#[test] +fn test_delegate_with_auto_compound_updates_collator_state() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 10)]) + .with_candidates(vec![(1, 30)]) + .build() + .execute_with(|| { + let candidate_state = + ParachainStaking::candidate_info(1).expect("registered in genesis"); + assert_eq!(candidate_state.total_counted, 30); + let top_delegations = + ParachainStaking::top_delegations(1).expect("registered in genesis"); + assert!(top_delegations.delegations.is_empty()); + assert!(top_delegations.total.is_zero()); + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + 0, + 0, + 0 + )); + let candidate_state = + ParachainStaking::candidate_info(1).expect("just delegated => exists"); + assert_eq!(candidate_state.total_counted, 40); + let top_delegations = + ParachainStaking::top_delegations(1).expect("just delegated => exists"); + assert_eq!(top_delegations.delegations[0].owner, 2); + assert_eq!(top_delegations.delegations[0].amount, 10); + assert_eq!(top_delegations.total, 10); + }); +} + +#[test] +fn test_delegate_with_auto_compound_can_delegate_immediately_after_other_join_candidates() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 20, 0)); + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 20, + Percent::from_percent(50), + 0, + 0, + 0 + )); + }); +} + +#[test] +fn test_delegate_with_auto_compound_can_delegate_to_other_if_revoking() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30), (3, 20), (4, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 4, + 10, + Percent::from_percent(50), + 0, + 0, + 2 + )); + }); +} + +#[test] +fn test_delegate_with_auto_compound_cannot_delegate_if_less_than_or_equal_lowest_bottom() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 10), + (3, 10), + (4, 10), + (5, 10), + (6, 10), + (7, 10), + (8, 10), + (9, 10), + (10, 10), + (11, 10), + ]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 10), + (4, 1, 10), + (5, 1, 10), + (6, 1, 10), + (8, 1, 10), + (9, 1, 10), + (10, 1, 10), + ]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(11), + 1, + 10, + Percent::from_percent(50), + 8, + 0, + 0 + ), + Error::::CannotDelegateLessThanOrEqualToLowestBottomWhenFull + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_can_delegate_if_greater_than_lowest_bottom() { + ExtBuilder::default() + .with_balances(vec![ + (1, 20), + (2, 10), + (3, 10), + (4, 10), + (5, 10), + (6, 10), + (7, 10), + (8, 10), + (9, 10), + (10, 10), + (11, 11), + ]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 10), + (4, 1, 10), + (5, 1, 10), + (6, 1, 10), + (8, 1, 10), + (9, 1, 10), + (10, 1, 10), + ]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(11), + 1, + 11, + Percent::from_percent(50), + 8, + 0, + 0 + )); + assert_events_emitted!(Event::DelegationKicked { + delegator: 10, + candidate: 1, + unstaked_amount: 10 + }); + assert_events_emitted!(Event::DelegatorLeft { delegator: 10, unstaked_amount: 10 }); + }); +} + +#[test] +fn test_delegate_with_auto_compound_can_still_delegate_to_other_if_leaving() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 20), (3, 20)]) + .with_candidates(vec![(1, 20), (3, 20)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 3, + 10, + Percent::from_percent(50), + 0, + 0, + 1 + ),); + }); +} + +#[test] +fn test_delegate_with_auto_compound_cannot_delegate_if_candidate() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30)]) + .with_candidates(vec![(1, 20), (2, 20)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + 0, + 0, + 0 + ), + Error::::CandidateExists + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_cannot_delegate_if_already_delegated() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 30)]) + .with_candidates(vec![(1, 20)]) + .with_delegations(vec![(2, 1, 20)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 1, + 10, + Percent::from_percent(50), + 0, + 1, + 1 + ), + Error::::AlreadyDelegatedCandidate + ); + }); +} + +#[test] +fn test_delegate_with_auto_compound_cannot_delegate_more_than_max_delegations() { + ExtBuilder::default() + .with_balances(vec![(1, 20), (2, 50), (3, 20), (4, 20), (5, 20), (6, 20)]) + .with_candidates(vec![(1, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10), (2, 4, 10), (2, 5, 10)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::delegate_with_auto_compound( + RuntimeOrigin::signed(2), + 6, + 10, + Percent::from_percent(50), + 0, + 0, + 4 + ), + Error::::ExceedMaxDelegationsPerDelegator, + ); + }); +} + +#[test] +fn test_delegate_skips_auto_compound_storage_but_emits_event_for_zero_auto_compound() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 30)]) + .with_candidates(vec![(1, 30)]) + .with_auto_compounding_delegations(vec![(3, 1, 10, Percent::from_percent(50))]) + .build() + .execute_with(|| { + // We already have an auto-compounding delegation from 3 -> 1, so the hint validation + // would cause a failure if the auto-compounding isn't skipped properly. + assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 1, 0,)); + assert_eq!(1, ParachainStaking::auto_compounding_delegations(1).len(),); + assert_events_eq!(Event::Delegation { + delegator: 2, + locked_amount: 10, + candidate: 1, + delegator_position: DelegatorAdded::AddedToTop { new_total: 50 }, + auto_compound: Percent::zero(), + }); + }); +} + +#[test] +fn test_on_initialize_weights() { + use crate::{ + mock::System, + weights::{SubstrateWeight as PalletWeights, WeightInfo}, + *, + }; + use frame_support::{pallet_prelude::*, weights::constants::RocksDbWeight}; + + // generate balance, candidate, and delegation vecs to "fill" out delegations + let mut balances = Vec::new(); + let mut candidates = Vec::new(); + let mut delegations = Vec::new(); + + for collator in 1..30 { + balances.push((collator, 100)); + candidates.push((collator, 10)); + let starting_delegator = collator * 1000; + for delegator in starting_delegator..starting_delegator + 300 { + balances.push((delegator, 100)); + delegations.push((delegator, collator, 10)); + } + } + + ExtBuilder::default() + .with_balances(balances) + .with_candidates(candidates) + .with_delegations(delegations) + .build() + .execute_with(|| { + let weight = ParachainStaking::on_initialize(1); + + // TODO: build this with proper db reads/writes + assert_eq!(Weight::from_parts(331000000, 1832), weight); + + // roll to the end of the round, then run on_init again, we should see round change... + roll_to_round_end(3); + set_author(2, 1, 100); // must set some points for prepare_staking_payouts + System::set_block_number(System::block_number() + 1); + let block = System::block_number() + 1; + let weight = ParachainStaking::on_initialize(block); + + // the total on_init weight during our round change. this number is taken from running + // the fn with a given weights.rs benchmark, so will need to be updated as benchmarks + // change. + // + // following this assertion, we add individual weights together to show that we can + // derive this number independently. + let expected_on_init = 2222282921; + assert_eq!(Weight::from_parts(expected_on_init, 40062), weight); + + // assemble weight manually to ensure it is well understood + let mut expected_weight = 0u64; + expected_weight += PalletWeights::::base_on_initialize().ref_time(); + expected_weight += PalletWeights::::prepare_staking_payouts().ref_time(); + + // TODO: this should be the same as >. I believe this relates to + // genesis building + let num_avg_delegations = 8; + expected_weight += PalletWeights::::select_top_candidates( + >::get(), + num_avg_delegations, + ) + .ref_time(); + // SlotProvider read + expected_weight += RocksDbWeight::get().reads_writes(1, 0).ref_time(); + // Round and Staked writes, done in on-round-change code block inside on_initialize() + expected_weight += RocksDbWeight::get().reads_writes(0, 2).ref_time(); + // more reads/writes manually accounted for for on_finalize + expected_weight += RocksDbWeight::get().reads_writes(3, 2).ref_time(); + + assert_eq!(Weight::from_parts(expected_weight, 40062), weight); + assert_eq!(expected_on_init, expected_weight); // magic number == independent accounting + }); +} + +#[test] +fn test_compute_top_candidates_is_stable() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 30), (3, 30), (4, 30), (5, 30), (6, 30)]) + .with_candidates(vec![(1, 30), (2, 30), (3, 30), (4, 30), (5, 30), (6, 30)]) + .build() + .execute_with(|| { + // There are 6 candidates with equal amount, but only 5 can be selected + assert_eq!(ParachainStaking::candidate_pool().0.len(), 6); + assert_eq!(ParachainStaking::total_selected(), 5); + // Returns the 5 candidates with greater AccountId, because they are iterated in reverse + assert_eq!(ParachainStaking::compute_top_candidates(), vec![2, 3, 4, 5, 6]); + }); +} + +#[test] +fn test_removed_calls() { + ExtBuilder::default().build().execute_with(|| { + assert_err!( + ParachainStaking::removed_call_19(RuntimeOrigin::root()), + Error::::RemovedCall + ); + assert_err!( + ParachainStaking::removed_call_20(RuntimeOrigin::root()), + Error::::RemovedCall + ); + assert_err!( + ParachainStaking::removed_call_21(RuntimeOrigin::root()), + Error::::RemovedCall + ); + }); +} + +#[test] +fn rewards_should_be_constant_when_annual_range_is_fix() { + let collator = 2; + ExtBuilder::default() + .with_balances(vec![(collator, 30)]) + .with_candidates(vec![(collator, 30)]) + .with_rewards_account(10, 1000000000) + .with_inflation(InflationInfo { + expect: Range { min: 0, ideal: 0, max: 0 }, + annual: Range { + min: Perbill::from_perthousand(75), + ideal: Perbill::from_perthousand(75), + max: Perbill::from_perthousand(75), + }, + round: Range { min: Perbill::zero(), ideal: Perbill::zero(), max: Perbill::zero() }, + }) + .build() + .execute_with(|| { + let rewards_delay = mock::RewardPaymentDelay::get(); + + // let's check the first 100 rounds + for i in 1..=100 { + set_author(i, collator, 100); + roll_to_round_begin(i + rewards_delay); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: collator, rewards: 71 }); + } + }); +} + +#[test] +fn collator_rewards_consistency_over_fixed_annual_range() { + let col = 2; + let col_1 = 3; + let col_stake = 30; + let col_1_stake = 30; + ExtBuilder::default() + .with_balances(vec![(col, 30), (col_1, 30)]) + .with_candidates(vec![(col, col_stake), (col_1, col_1_stake)]) + .build() + .execute_with(|| { + // check the blocks per round + let blocks_per_round = ParachainStaking::round().length; + assert_eq!(blocks_per_round, 5); + + for round in 2..=103 { + // rolling to check the rewards + roll_to_round_begin(round); + assert_events_eq!( + Event::CollatorChosen { + round, + collator_account: col, + total_exposed_amount: col_stake, + }, + Event::CollatorChosen { + round, + collator_account: col_1, + total_exposed_amount: col_1_stake + }, + Event::NewRound { + starting_block: (blocks_per_round * (round - 1)).into(), + round, + selected_collators_number: 2, + total_balance: col_stake + col_1_stake, + }, + ); + } + }); +} + +#[test] +fn rewards_with_2_collators() { + let col = 2; + let col_1 = 3; + let col_stake = 30; + let col_1_stake = 30; + ExtBuilder::default() + .with_balances(vec![(col, 30), (col_1, 30)]) + .with_candidates(vec![(col, col_stake), (col_1, col_1_stake)]) + .with_rewards_account(10, 1000000000) + .with_inflation(InflationInfo { + expect: Range { min: 0, ideal: 0, max: 0 }, + annual: Range { + min: Perbill::from_perthousand(75), + ideal: Perbill::from_perthousand(75), + max: Perbill::from_perthousand(75), + }, + round: Range { min: Perbill::zero(), ideal: Perbill::zero(), max: Perbill::zero() }, + }) + .build() + .execute_with(|| { + let rewards_delay = mock::RewardPaymentDelay::get(); + + // check the blocks per round + let blocks_per_round = ParachainStaking::round().length; + assert_eq!(blocks_per_round, 5); + + let round = 1; + set_author(round, col, 100); + let round = round + rewards_delay; + roll_to_round_begin(round); + roll_blocks(1); + assert_no_events!(); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: col, rewards: 71 },); + roll_blocks(1); + assert_no_events!(); + roll_blocks(1); + assert_no_events!(); + + // same points + let round = 5; + set_author(round, col, 100); + set_author(round, col_1, 100); + let round = round + rewards_delay; + roll_to_round_begin(round); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: col_1, rewards: 35 },); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: col, rewards: 35 },); + roll_blocks(1); + assert_no_events!(); + roll_blocks(1); + assert_no_events!(); + + // same points + let round = 10; + set_author(round, col, 200); + set_author(round, col_1, 100); + let round = round + rewards_delay; + roll_to_round_begin(round); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: col_1, rewards: 24 },); + roll_blocks(1); + assert_events_eq!(Event::Rewarded { account: col, rewards: 47 },); + roll_blocks(1); + assert_no_events!(); + roll_blocks(1); + assert_no_events!(); + }); +} diff --git a/pallets/parachain-staking-old/src/traits.rs b/pallets/parachain-staking-old/src/traits.rs new file mode 100644 index 000000000..a6fb0927b --- /dev/null +++ b/pallets/parachain-staking-old/src/traits.rs @@ -0,0 +1,83 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! traits for parachain-staking + +use crate::{weights::WeightInfo, BalanceOf}; +use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::Weight}; +use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; + +pub trait OnCollatorPayout { + fn on_collator_payout( + for_round: crate::RoundIndex, + collator_id: AccountId, + amount: Balance, + ) -> Weight; +} +impl OnCollatorPayout for () { + fn on_collator_payout( + _for_round: crate::RoundIndex, + _collator_id: AccountId, + _amount: Balance, + ) -> Weight { + Weight::zero() + } +} + +pub trait OnNewRound { + fn on_new_round(round_index: crate::RoundIndex) -> Weight; +} +impl OnNewRound for () { + fn on_new_round(_round_index: crate::RoundIndex) -> Weight { + Weight::zero() + } +} + +/// Defines the behavior to payout the block producer reward. +pub trait PayoutReward { + /// Send amount to the balance of the specified account (collator). + /// and corresponding weight consumed is returned. + fn payout_collator_rewards( + round_index: crate::RoundIndex, + collator: Runtime::AccountId, + amount: BalanceOf, + ) -> Weight; + + /// Send amount to the free balance of the specified account (destination). + /// If the account (destination) does not exist, the operation is not carried out, + /// and an error is returned instead. + fn payout( + destination: &Runtime::AccountId, + amount: BalanceOf, + ) -> Result, DispatchError>; +} + +pub trait OnInactiveCollator { + fn on_inactive_collator( + collator_id: Runtime::AccountId, + round: crate::RoundIndex, + ) -> Result>; +} + +impl OnInactiveCollator for () { + fn on_inactive_collator( + collator_id: ::AccountId, + _round: crate::RoundIndex, + ) -> Result> { + crate::Pallet::::go_offline_inner(collator_id)?; + Ok(::WeightInfo::go_offline(crate::MAX_CANDIDATES)) + } +} diff --git a/pallets/parachain-staking-old/src/types.rs b/pallets/parachain-staking-old/src/types.rs new file mode 100644 index 000000000..ae98ec805 --- /dev/null +++ b/pallets/parachain-staking-old/src/types.rs @@ -0,0 +1,1642 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + +//! Types for parachain-staking + +use crate::{ + auto_compound::AutoCompoundDelegations, set::OrderedSet, BalanceOf, BottomDelegations, + CandidateInfo, Config, DelegatorState, Error, Event, Pallet, Round, RoundIndex, TopDelegations, + Total, COLLATOR_LOCK_ID, DELEGATOR_LOCK_ID, +}; +use frame_support::{ + pallet_prelude::*, + traits::{tokens::WithdrawReasons, LockableCurrency}, +}; +use parity_scale_codec::{Decode, Encode}; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Saturating, Zero}, + Perbill, Percent, RuntimeDebug, +}; +use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; + +pub struct CountedDelegations { + pub uncounted_stake: BalanceOf, + pub rewardable_delegations: Vec>>, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct Bond { + pub owner: AccountId, + pub amount: Balance, +} + +impl Default for Bond { + fn default() -> Bond { + Bond { + owner: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"), + amount: B::default(), + } + } +} + +impl Bond { + pub fn from_owner(owner: A) -> Self { + Bond { owner, amount: B::default() } + } +} + +impl Eq for Bond {} + +impl Ord for Bond { + fn cmp(&self, other: &Self) -> Ordering { + self.owner.cmp(&other.owner) + } +} + +impl PartialOrd for Bond { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for Bond { + fn eq(&self, other: &Self) -> bool { + self.owner == other.owner + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)] +/// The activity status of the collator +pub enum CollatorStatus { + /// Committed to be online and producing valid blocks (not equivocating) + #[default] + Active, + /// Temporarily inactive and excused for inactivity + Idle, + /// Bonded until the inner round + Leaving(RoundIndex), +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct BondWithAutoCompound { + pub owner: AccountId, + pub amount: Balance, + pub auto_compound: Percent, +} + +impl Default for BondWithAutoCompound { + fn default() -> BondWithAutoCompound { + BondWithAutoCompound { + owner: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"), + amount: B::default(), + auto_compound: Percent::zero(), + } + } +} + +#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] +/// Snapshot of collator state at the start of the round for which they are selected +pub struct CollatorSnapshot { + /// The total value locked by the collator. + pub bond: Balance, + + /// The rewardable delegations. This list is a subset of total delegators, where certain + /// delegators are adjusted based on their scheduled + /// [DelegationChange::Revoke] or [DelegationChange::Decrease] action. + pub delegations: Vec>, + + /// The total counted value locked for the collator, including the self bond + total staked by + /// top delegators. + pub total: Balance, +} + +impl PartialEq for CollatorSnapshot { + fn eq(&self, other: &Self) -> bool { + let must_be_true = self.bond == other.bond && self.total == other.total; + if !must_be_true { + return false; + } + for ( + BondWithAutoCompound { owner: o1, amount: a1, auto_compound: c1 }, + BondWithAutoCompound { owner: o2, amount: a2, auto_compound: c2 }, + ) in self.delegations.iter().zip(other.delegations.iter()) + { + if o1 != o2 || a1 != a2 || c1 != c2 { + return false; + } + } + true + } +} + +impl Default for CollatorSnapshot { + fn default() -> CollatorSnapshot { + CollatorSnapshot { bond: B::default(), delegations: Vec::new(), total: B::default() } + } +} + +#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo)] +/// Info needed to make delayed payments to stakers after round end +pub struct DelayedPayout { + /// Total round reward (result of compute_issuance() at round end) + pub round_issuance: Balance, + /// The total inflation paid this round to stakers (e.g. less parachain bond fund) + pub total_staking_reward: Balance, + /// Snapshot of collator commission rate at the end of the round + pub collator_commission: Perbill, +} + +#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] +/// DEPRECATED +/// Collator state with commission fee, bonded stake, and delegations +pub struct Collator2 { + /// The account of this collator + pub id: AccountId, + /// This collator's self stake. + pub bond: Balance, + /// Set of all nominator AccountIds (to prevent >1 nomination per AccountId) + pub nominators: OrderedSet, + /// Top T::MaxDelegatorsPerCollator::get() nominators, ordered greatest to least + pub top_nominators: Vec>, + /// Bottom nominators (unbounded), ordered least to greatest + pub bottom_nominators: Vec>, + /// Sum of top delegations + self.bond + pub total_counted: Balance, + /// Sum of all delegations + self.bond = (total_counted + uncounted) + pub total_backing: Balance, + /// Current status of the collator + pub state: CollatorStatus, +} + +impl From> for CollatorCandidate { + fn from(other: Collator2) -> CollatorCandidate { + CollatorCandidate { + id: other.id, + bond: other.bond, + delegators: other.nominators, + top_delegations: other.top_nominators, + bottom_delegations: other.bottom_nominators, + total_counted: other.total_counted, + total_backing: other.total_backing, + request: None, + state: other.state, + } + } +} + +#[derive(PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)] +/// Request scheduled to change the collator candidate self-bond +pub struct CandidateBondLessRequest { + pub amount: Balance, + pub when_executable: RoundIndex, +} + +#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] +/// DEPRECATED, replaced by `CandidateMetadata` and two storage instances of `Delegations` +/// Collator candidate state with self bond + delegations +pub struct CollatorCandidate { + /// The account of this collator + pub id: AccountId, + /// This collator's self stake. + pub bond: Balance, + /// Set of all delegator AccountIds (to prevent >1 delegation per AccountId) + pub delegators: OrderedSet, + /// Top T::MaxDelegatorsPerCollator::get() delegations, ordered greatest to least + pub top_delegations: Vec>, + /// Bottom delegations (unbounded), ordered least to greatest + pub bottom_delegations: Vec>, + /// Sum of top delegations + self.bond + pub total_counted: Balance, + /// Sum of all delegations + self.bond = (total_counted + uncounted) + pub total_backing: Balance, + /// Maximum 1 pending request to decrease candidate self bond at any given time + pub request: Option>, + /// Current status of the collator + pub state: CollatorStatus, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +/// Type for top and bottom delegation storage item +pub struct Delegations { + pub delegations: Vec>, + pub total: Balance, +} + +impl Default for Delegations { + fn default() -> Delegations { + Delegations { delegations: Vec::new(), total: B::default() } + } +} + +impl + Delegations +{ + pub fn sort_greatest_to_least(&mut self) { + self.delegations.sort_by(|a, b| b.amount.cmp(&a.amount)); + } + /// Insert sorted greatest to least and increase .total accordingly + /// Insertion respects first come first serve so new delegations are pushed after existing + /// delegations if the amount is the same + pub fn insert_sorted_greatest_to_least(&mut self, delegation: Bond) { + self.total = self.total.saturating_add(delegation.amount); + // if delegations nonempty && last_element == delegation.amount => push input and return + if !self.delegations.is_empty() { + // if last_element == delegation.amount => push the delegation and return early + if self.delegations[self.delegations.len() - 1].amount == delegation.amount { + self.delegations.push(delegation); + // early return + return; + } + } + // else binary search insertion + match self.delegations.binary_search_by(|x| delegation.amount.cmp(&x.amount)) { + // sorted insertion on sorted vec + // enforces first come first serve for equal bond amounts + Ok(i) => { + let mut new_index = i + 1; + while new_index <= (self.delegations.len() - 1) { + if self.delegations[new_index].amount == delegation.amount { + new_index = new_index.saturating_add(1); + } else { + self.delegations.insert(new_index, delegation); + return; + } + } + self.delegations.push(delegation) + }, + Err(i) => self.delegations.insert(i, delegation), + } + } + /// Return the capacity status for top delegations + pub fn top_capacity(&self) -> CapacityStatus { + match &self.delegations { + x if x.len() as u32 >= T::MaxTopDelegationsPerCandidate::get() => CapacityStatus::Full, + x if x.is_empty() => CapacityStatus::Empty, + _ => CapacityStatus::Partial, + } + } + /// Return the capacity status for bottom delegations + pub fn bottom_capacity(&self) -> CapacityStatus { + match &self.delegations { + x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => + CapacityStatus::Full, + x if x.is_empty() => CapacityStatus::Empty, + _ => CapacityStatus::Partial, + } + } + /// Return last delegation amount without popping the delegation + pub fn lowest_delegation_amount(&self) -> Balance { + self.delegations.last().map(|x| x.amount).unwrap_or(Balance::zero()) + } + /// Return highest delegation amount + pub fn highest_delegation_amount(&self) -> Balance { + self.delegations.first().map(|x| x.amount).unwrap_or(Balance::zero()) + } +} + +#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +/// Capacity status for top or bottom delegations +pub enum CapacityStatus { + /// Reached capacity + Full, + /// Empty aka contains no delegations + Empty, + /// Partially full (nonempty and not full) + Partial, +} + +#[derive(Encode, Decode, RuntimeDebug, TypeInfo)] +/// All candidate info except the top and bottom delegations +pub struct CandidateMetadata { + /// This candidate's self bond amount + pub bond: Balance, + /// Total number of delegations to this candidate + pub delegation_count: u32, + /// Self bond + sum of top delegations + pub total_counted: Balance, + /// The smallest top delegation amount + pub lowest_top_delegation_amount: Balance, + /// The highest bottom delegation amount + pub highest_bottom_delegation_amount: Balance, + /// The smallest bottom delegation amount + pub lowest_bottom_delegation_amount: Balance, + /// Capacity status for top delegations + pub top_capacity: CapacityStatus, + /// Capacity status for bottom delegations + pub bottom_capacity: CapacityStatus, + /// Maximum 1 pending request to decrease candidate self bond at any given time + pub request: Option>, + /// Current status of the collator + pub status: CollatorStatus, +} + +impl< + Balance: Copy + + Zero + + PartialOrd + + sp_std::ops::AddAssign + + sp_std::ops::SubAssign + + sp_std::ops::Sub + + sp_std::fmt::Debug + + Saturating, + > CandidateMetadata +{ + pub fn new(bond: Balance) -> Self { + CandidateMetadata { + bond, + delegation_count: 0u32, + total_counted: bond, + lowest_top_delegation_amount: Zero::zero(), + highest_bottom_delegation_amount: Zero::zero(), + lowest_bottom_delegation_amount: Zero::zero(), + top_capacity: CapacityStatus::Empty, + bottom_capacity: CapacityStatus::Empty, + request: None, + status: CollatorStatus::Active, + } + } + pub fn is_active(&self) -> bool { + matches!(self.status, CollatorStatus::Active) + } + pub fn is_leaving(&self) -> bool { + matches!(self.status, CollatorStatus::Leaving(_)) + } + pub fn schedule_leave(&mut self) -> Result<(RoundIndex, RoundIndex), DispatchError> { + ensure!(!self.is_leaving(), Error::::CandidateAlreadyLeaving); + let now = >::get().current; + let when = now + T::LeaveCandidatesDelay::get(); + self.status = CollatorStatus::Leaving(when); + Ok((now, when)) + } + pub fn can_leave(&self) -> DispatchResult { + if let CollatorStatus::Leaving(when) = self.status { + ensure!(>::get().current >= when, Error::::CandidateCannotLeaveYet); + Ok(()) + } else { + Err(Error::::CandidateNotLeaving.into()) + } + } + pub fn go_offline(&mut self) { + self.status = CollatorStatus::Idle; + } + pub fn go_online(&mut self) { + self.status = CollatorStatus::Active; + } + pub fn bond_more(&mut self, who: T::AccountId, more: Balance) -> DispatchResult + where + BalanceOf: From, + { + ensure!( + >::get_collator_stakable_free_balance(&who) >= more.into(), + Error::::InsufficientBalance + ); + let new_total = >::get().saturating_add(more.into()); + >::put(new_total); + self.bond = self.bond.saturating_add(more); + T::Currency::set_lock( + COLLATOR_LOCK_ID, + &who.clone(), + self.bond.into(), + WithdrawReasons::all(), + ); + self.total_counted = self.total_counted.saturating_add(more); + >::deposit_event(Event::CandidateBondedMore { + candidate: who.clone(), + amount: more.into(), + new_total_bond: self.bond.into(), + }); + Ok(()) + } + + pub fn bond_less(&mut self, who: T::AccountId, amount: Balance) + where + BalanceOf: From, + { + let new_total_staked = >::get().saturating_sub(amount.into()); + >::put(new_total_staked); + self.bond = self.bond.saturating_sub(amount); + if self.bond.is_zero() { + T::Currency::remove_lock(COLLATOR_LOCK_ID, &who); + } else { + T::Currency::set_lock(COLLATOR_LOCK_ID, &who, self.bond.into(), WithdrawReasons::all()); + } + self.total_counted = self.total_counted.saturating_sub(amount); + let event = Event::CandidateBondedLess { + candidate: who.clone(), + amount: amount.into(), + new_bond: self.bond.into(), + }; + // update candidate pool value because it must change if self bond changes + if self.is_active() { + Pallet::::update_active(who, self.total_counted.into()); + } + Pallet::::deposit_event(event); + } + + /// Schedule executable decrease of collator candidate self bond + /// Returns the round at which the collator can execute the pending request + pub fn schedule_bond_less( + &mut self, + less: Balance, + ) -> Result + where + BalanceOf: Into, + { + // ensure no pending request + ensure!(self.request.is_none(), Error::::PendingCandidateRequestAlreadyExists); + // ensure bond above min after decrease + ensure!(self.bond > less, Error::::CandidateBondBelowMin); + ensure!( + self.bond - less >= T::MinCandidateStk::get().into(), + Error::::CandidateBondBelowMin + ); + let when_executable = >::get().current + T::CandidateBondLessDelay::get(); + self.request = Some(CandidateBondLessRequest { amount: less, when_executable }); + Ok(when_executable) + } + /// Execute pending request to decrease the collator self bond + /// Returns the event to be emitted + pub fn execute_bond_less(&mut self, who: T::AccountId) -> DispatchResult + where + BalanceOf: From, + { + let request = self.request.ok_or(Error::::PendingCandidateRequestsDNE)?; + ensure!( + request.when_executable <= >::get().current, + Error::::PendingCandidateRequestNotDueYet + ); + self.bond_less::(who.clone(), request.amount); + // reset s.t. no pending request + self.request = None; + Ok(()) + } + + /// Cancel candidate bond less request + pub fn cancel_bond_less(&mut self, who: T::AccountId) -> DispatchResult + where + BalanceOf: From, + { + let request = self.request.ok_or(Error::::PendingCandidateRequestsDNE)?; + let event = Event::CancelledCandidateBondLess { + candidate: who, + amount: request.amount.into(), + execute_round: request.when_executable, + }; + self.request = None; + Pallet::::deposit_event(event); + Ok(()) + } + /// Reset top delegations metadata + pub fn reset_top_data( + &mut self, + candidate: T::AccountId, + top_delegations: &Delegations>, + ) where + BalanceOf: Into + From, + { + self.lowest_top_delegation_amount = top_delegations.lowest_delegation_amount().into(); + self.top_capacity = top_delegations.top_capacity::(); + let old_total_counted = self.total_counted; + self.total_counted = self.bond.saturating_add(top_delegations.total.into()); + // CandidatePool value for candidate always changes if top delegations total changes + // so we moved the update into this function to deduplicate code and patch a bug that + // forgot to apply the update when increasing top delegation + if old_total_counted != self.total_counted && self.is_active() { + Pallet::::update_active(candidate, self.total_counted.into()); + } + } + /// Reset bottom delegations metadata + pub fn reset_bottom_data( + &mut self, + bottom_delegations: &Delegations>, + ) where + BalanceOf: Into, + { + self.lowest_bottom_delegation_amount = bottom_delegations.lowest_delegation_amount().into(); + self.highest_bottom_delegation_amount = + bottom_delegations.highest_delegation_amount().into(); + self.bottom_capacity = bottom_delegations.bottom_capacity::(); + } + /// Add delegation + /// Returns whether delegator was added and an optional negative total counted remainder + /// for if a bottom delegation was kicked + /// MUST ensure no delegation exists for this candidate in the `DelegatorState` before call + pub fn add_delegation( + &mut self, + candidate: &T::AccountId, + delegation: Bond>, + ) -> Result<(DelegatorAdded, Option), DispatchError> + where + BalanceOf: Into + From, + { + let mut less_total_staked = None; + let delegator_added = match self.top_capacity { + CapacityStatus::Full => { + // top is full, insert into top iff the lowest_top < amount + if self.lowest_top_delegation_amount < delegation.amount.into() { + // bumps lowest top to the bottom inside this function call + less_total_staked = self.add_top_delegation::(candidate, delegation); + DelegatorAdded::AddedToTop { new_total: self.total_counted } + } else { + // if bottom is full, only insert if greater than lowest bottom (which will + // be bumped out) + if matches!(self.bottom_capacity, CapacityStatus::Full) { + ensure!( + delegation.amount.into() > self.lowest_bottom_delegation_amount, + Error::::CannotDelegateLessThanOrEqualToLowestBottomWhenFull + ); + // need to subtract from total staked + less_total_staked = Some(self.lowest_bottom_delegation_amount); + } + // insert into bottom + self.add_bottom_delegation::(false, candidate, delegation); + DelegatorAdded::AddedToBottom + } + }, + // top is either empty or partially full + _ => { + self.add_top_delegation::(candidate, delegation); + DelegatorAdded::AddedToTop { new_total: self.total_counted } + }, + }; + Ok((delegator_added, less_total_staked)) + } + /// Add delegation to top delegation + /// Returns Option + /// Only call if lowest top delegation is less than delegation.amount || !top_full + pub fn add_top_delegation( + &mut self, + candidate: &T::AccountId, + delegation: Bond>, + ) -> Option + where + BalanceOf: Into + From, + { + let mut less_total_staked = None; + let mut top_delegations = >::get(candidate) + .expect("CandidateInfo existence => TopDelegations existence"); + let max_top_delegations_per_candidate = T::MaxTopDelegationsPerCandidate::get(); + if top_delegations.delegations.len() as u32 == max_top_delegations_per_candidate { + // pop lowest top delegation + let new_bottom_delegation = top_delegations.delegations.pop().expect(""); + top_delegations.total = + top_delegations.total.saturating_sub(new_bottom_delegation.amount); + if matches!(self.bottom_capacity, CapacityStatus::Full) { + less_total_staked = Some(self.lowest_bottom_delegation_amount); + } + self.add_bottom_delegation::(true, candidate, new_bottom_delegation); + } + // insert into top + top_delegations.insert_sorted_greatest_to_least(delegation); + // update candidate info + self.reset_top_data::(candidate.clone(), &top_delegations); + if less_total_staked.is_none() { + // only increment delegation count if we are not kicking a bottom delegation + self.delegation_count = self.delegation_count.saturating_add(1u32); + } + >::insert(candidate, top_delegations); + less_total_staked + } + /// Add delegation to bottom delegations + /// Check before call that if capacity is full, inserted delegation is higher than lowest + /// bottom delegation (and if so, need to adjust the total storage item) + /// CALLER MUST ensure(lowest_bottom_to_be_kicked.amount < delegation.amount) + pub fn add_bottom_delegation( + &mut self, + bumped_from_top: bool, + candidate: &T::AccountId, + delegation: Bond>, + ) where + BalanceOf: Into + From, + { + let mut bottom_delegations = >::get(candidate) + .expect("CandidateInfo existence => BottomDelegations existence"); + // if bottom is full, kick the lowest bottom (which is expected to be lower than input + // as per check) + let increase_delegation_count = if bottom_delegations.delegations.len() as u32 == + T::MaxBottomDelegationsPerCandidate::get() + { + let lowest_bottom_to_be_kicked = bottom_delegations + .delegations + .pop() + .expect("if at full capacity (>0), then >0 bottom delegations exist; qed"); + // EXPECT lowest_bottom_to_be_kicked.amount < delegation.amount enforced by caller + // if lowest_bottom_to_be_kicked.amount == delegation.amount, we will still kick + // the lowest bottom to enforce first come first served + bottom_delegations.total = + bottom_delegations.total.saturating_sub(lowest_bottom_to_be_kicked.amount); + // update delegator state + // total staked is updated via propagation of lowest bottom delegation amount prior + // to call + let mut delegator_state = >::get(&lowest_bottom_to_be_kicked.owner) + .expect("Delegation existence => DelegatorState existence"); + let leaving = delegator_state.delegations.0.len() == 1usize; + delegator_state.rm_delegation::(candidate); + >::delegation_remove_request_with_state( + candidate, + &lowest_bottom_to_be_kicked.owner, + &mut delegator_state, + ); + >::remove_auto_compound( + candidate, + &lowest_bottom_to_be_kicked.owner, + ); + + Pallet::::deposit_event(Event::DelegationKicked { + delegator: lowest_bottom_to_be_kicked.owner.clone(), + candidate: candidate.clone(), + unstaked_amount: lowest_bottom_to_be_kicked.amount, + }); + if leaving { + >::remove(&lowest_bottom_to_be_kicked.owner); + Pallet::::deposit_event(Event::DelegatorLeft { + delegator: lowest_bottom_to_be_kicked.owner, + unstaked_amount: lowest_bottom_to_be_kicked.amount, + }); + } else { + >::insert(&lowest_bottom_to_be_kicked.owner, delegator_state); + } + false + } else { + !bumped_from_top + }; + // only increase delegation count if new bottom delegation (1) doesn't come from top && + // (2) doesn't pop the lowest delegation from the bottom + if increase_delegation_count { + self.delegation_count = self.delegation_count.saturating_add(1u32); + } + bottom_delegations.insert_sorted_greatest_to_least(delegation); + self.reset_bottom_data::(&bottom_delegations); + >::insert(candidate, bottom_delegations); + } + /// Remove delegation + /// Removes from top if amount is above lowest top or top is not full + /// Return Ok(if_total_counted_changed) + pub fn rm_delegation_if_exists( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + amount: Balance, + ) -> Result + where + BalanceOf: Into + From, + { + let amount_geq_lowest_top = amount >= self.lowest_top_delegation_amount; + let top_is_not_full = !matches!(self.top_capacity, CapacityStatus::Full); + let lowest_top_eq_highest_bottom = + self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount; + let delegation_dne_err: DispatchError = Error::::DelegationDNE.into(); + if top_is_not_full || (amount_geq_lowest_top && !lowest_top_eq_highest_bottom) { + self.rm_top_delegation::(candidate, delegator) + } else if amount_geq_lowest_top && lowest_top_eq_highest_bottom { + let result = self.rm_top_delegation::(candidate, delegator.clone()); + if result == Err(delegation_dne_err) { + // worst case removal + self.rm_bottom_delegation::(candidate, delegator) + } else { + result + } + } else { + self.rm_bottom_delegation::(candidate, delegator) + } + } + /// Remove top delegation, bumps top bottom delegation if exists + pub fn rm_top_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + ) -> Result + where + BalanceOf: Into + From, + { + let old_total_counted = self.total_counted; + // remove top delegation + let mut top_delegations = >::get(candidate) + .expect("CandidateInfo exists => TopDelegations exists"); + let mut actual_amount_option: Option> = None; + top_delegations.delegations = top_delegations + .delegations + .clone() + .into_iter() + .filter(|d| { + if d.owner != delegator { + true + } else { + actual_amount_option = Some(d.amount); + false + } + }) + .collect(); + let actual_amount = actual_amount_option.ok_or(Error::::DelegationDNE)?; + top_delegations.total = top_delegations.total.saturating_sub(actual_amount); + // if bottom nonempty => bump top bottom to top + if !matches!(self.bottom_capacity, CapacityStatus::Empty) { + let mut bottom_delegations = + >::get(candidate).expect("bottom is nonempty as just checked"); + // expect already stored greatest to least by bond amount + let highest_bottom_delegation = bottom_delegations.delegations.remove(0); + bottom_delegations.total = + bottom_delegations.total.saturating_sub(highest_bottom_delegation.amount); + self.reset_bottom_data::(&bottom_delegations); + >::insert(candidate, bottom_delegations); + // insert highest bottom into top delegations + top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation); + } + // update candidate info + self.reset_top_data::(candidate.clone(), &top_delegations); + self.delegation_count = self.delegation_count.saturating_sub(1u32); + >::insert(candidate, top_delegations); + // return whether total counted changed + Ok(old_total_counted == self.total_counted) + } + /// Remove bottom delegation + /// Returns if_total_counted_changed: bool + pub fn rm_bottom_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + ) -> Result + where + BalanceOf: Into, + { + // remove bottom delegation + let mut bottom_delegations = >::get(candidate) + .expect("CandidateInfo exists => BottomDelegations exists"); + let mut actual_amount_option: Option> = None; + bottom_delegations.delegations = bottom_delegations + .delegations + .clone() + .into_iter() + .filter(|d| { + if d.owner != delegator { + true + } else { + actual_amount_option = Some(d.amount); + false + } + }) + .collect(); + let actual_amount = actual_amount_option.ok_or(Error::::DelegationDNE)?; + bottom_delegations.total = bottom_delegations.total.saturating_sub(actual_amount); + // update candidate info + self.reset_bottom_data::(&bottom_delegations); + self.delegation_count = self.delegation_count.saturating_sub(1u32); + >::insert(candidate, bottom_delegations); + Ok(false) + } + /// Increase delegation amount + pub fn increase_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + bond: BalanceOf, + more: BalanceOf, + ) -> Result + where + BalanceOf: Into + From, + { + let lowest_top_eq_highest_bottom = + self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount; + let bond_geq_lowest_top = bond.into() >= self.lowest_top_delegation_amount; + let delegation_dne_err: DispatchError = Error::::DelegationDNE.into(); + if bond_geq_lowest_top && !lowest_top_eq_highest_bottom { + // definitely in top + self.increase_top_delegation::(candidate, delegator.clone(), more) + } else if bond_geq_lowest_top && lowest_top_eq_highest_bottom { + // update top but if error then update bottom (because could be in bottom because + // lowest_top_eq_highest_bottom) + let result = self.increase_top_delegation::(candidate, delegator.clone(), more); + if result == Err(delegation_dne_err) { + self.increase_bottom_delegation::(candidate, delegator, bond, more) + } else { + result + } + } else { + self.increase_bottom_delegation::(candidate, delegator, bond, more) + } + } + /// Increase top delegation + pub fn increase_top_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + more: BalanceOf, + ) -> Result + where + BalanceOf: Into + From, + { + let mut top_delegations = >::get(candidate) + .expect("CandidateInfo exists => TopDelegations exists"); + let mut in_top = false; + top_delegations.delegations = top_delegations + .delegations + .clone() + .into_iter() + .map(|d| { + if d.owner != delegator { + d + } else { + in_top = true; + let new_amount = d.amount.saturating_add(more); + Bond { owner: d.owner, amount: new_amount } + } + }) + .collect(); + ensure!(in_top, Error::::DelegationDNE); + top_delegations.total = top_delegations.total.saturating_add(more); + top_delegations.sort_greatest_to_least(); + self.reset_top_data::(candidate.clone(), &top_delegations); + >::insert(candidate, top_delegations); + Ok(true) + } + /// Increase bottom delegation + pub fn increase_bottom_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + bond: BalanceOf, + more: BalanceOf, + ) -> Result + where + BalanceOf: Into + From, + { + let mut bottom_delegations = + >::get(candidate).ok_or(Error::::CandidateDNE)?; + let mut delegation_option: Option>> = None; + let in_top_after = if (bond.saturating_add(more)).into() > self.lowest_top_delegation_amount + { + // bump it from bottom + bottom_delegations.delegations = bottom_delegations + .delegations + .clone() + .into_iter() + .filter(|d| { + if d.owner != delegator { + true + } else { + delegation_option = Some(Bond { + owner: d.owner.clone(), + amount: d.amount.saturating_add(more), + }); + false + } + }) + .collect(); + let delegation = delegation_option.ok_or(Error::::DelegationDNE)?; + bottom_delegations.total = bottom_delegations.total.saturating_sub(bond); + // add it to top + let mut top_delegations = >::get(candidate) + .expect("CandidateInfo existence => TopDelegations existence"); + // if top is full, pop lowest top + if matches!(top_delegations.top_capacity::(), CapacityStatus::Full) { + // pop lowest top delegation + let new_bottom_delegation = top_delegations + .delegations + .pop() + .expect("Top capacity full => Exists at least 1 top delegation"); + top_delegations.total = + top_delegations.total.saturating_sub(new_bottom_delegation.amount); + bottom_delegations.insert_sorted_greatest_to_least(new_bottom_delegation); + } + // insert into top + top_delegations.insert_sorted_greatest_to_least(delegation); + self.reset_top_data::(candidate.clone(), &top_delegations); + >::insert(candidate, top_delegations); + true + } else { + let mut in_bottom = false; + // just increase the delegation + bottom_delegations.delegations = bottom_delegations + .delegations + .clone() + .into_iter() + .map(|d| { + if d.owner != delegator { + d + } else { + in_bottom = true; + Bond { owner: d.owner, amount: d.amount.saturating_add(more) } + } + }) + .collect(); + ensure!(in_bottom, Error::::DelegationDNE); + bottom_delegations.total = bottom_delegations.total.saturating_add(more); + bottom_delegations.sort_greatest_to_least(); + false + }; + self.reset_bottom_data::(&bottom_delegations); + >::insert(candidate, bottom_delegations); + Ok(in_top_after) + } + /// Decrease delegation + pub fn decrease_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + bond: Balance, + less: BalanceOf, + ) -> Result + where + BalanceOf: Into + From, + { + let lowest_top_eq_highest_bottom = + self.lowest_top_delegation_amount == self.highest_bottom_delegation_amount; + let bond_geq_lowest_top = bond >= self.lowest_top_delegation_amount; + let delegation_dne_err: DispatchError = Error::::DelegationDNE.into(); + if bond_geq_lowest_top && !lowest_top_eq_highest_bottom { + // definitely in top + self.decrease_top_delegation::(candidate, delegator.clone(), bond.into(), less) + } else if bond_geq_lowest_top && lowest_top_eq_highest_bottom { + // update top but if error then update bottom (because could be in bottom because + // lowest_top_eq_highest_bottom) + let result = + self.decrease_top_delegation::(candidate, delegator.clone(), bond.into(), less); + if result == Err(delegation_dne_err) { + self.decrease_bottom_delegation::(candidate, delegator, less) + } else { + result + } + } else { + self.decrease_bottom_delegation::(candidate, delegator, less) + } + } + /// Decrease top delegation + pub fn decrease_top_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + bond: BalanceOf, + less: BalanceOf, + ) -> Result + where + BalanceOf: Into + From, + { + // The delegation after the `decrease-delegation` will be strictly less than the + // highest bottom delegation + let bond_after_less_than_highest_bottom = + bond.saturating_sub(less).into() < self.highest_bottom_delegation_amount; + // The top delegations is full and the bottom delegations has at least one delegation + let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full) && + !matches!(self.bottom_capacity, CapacityStatus::Empty); + let mut top_delegations = + >::get(candidate).ok_or(Error::::CandidateDNE)?; + let in_top_after = if bond_after_less_than_highest_bottom && full_top_and_nonempty_bottom { + let mut delegation_option: Option>> = None; + // take delegation from top + top_delegations.delegations = top_delegations + .delegations + .clone() + .into_iter() + .filter(|d| { + if d.owner != delegator { + true + } else { + top_delegations.total = top_delegations.total.saturating_sub(d.amount); + delegation_option = Some(Bond { + owner: d.owner.clone(), + amount: d.amount.saturating_sub(less), + }); + false + } + }) + .collect(); + let delegation = delegation_option.ok_or(Error::::DelegationDNE)?; + // pop highest bottom by reverse and popping + let mut bottom_delegations = >::get(candidate) + .expect("CandidateInfo existence => BottomDelegations existence"); + let highest_bottom_delegation = bottom_delegations.delegations.remove(0); + bottom_delegations.total = + bottom_delegations.total.saturating_sub(highest_bottom_delegation.amount); + // insert highest bottom into top + top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation); + // insert previous top into bottom + bottom_delegations.insert_sorted_greatest_to_least(delegation); + self.reset_bottom_data::(&bottom_delegations); + >::insert(candidate, bottom_delegations); + false + } else { + // keep it in the top + let mut is_in_top = false; + top_delegations.delegations = top_delegations + .delegations + .clone() + .into_iter() + .map(|d| { + if d.owner != delegator { + d + } else { + is_in_top = true; + Bond { owner: d.owner, amount: d.amount.saturating_sub(less) } + } + }) + .collect(); + ensure!(is_in_top, Error::::DelegationDNE); + top_delegations.total = top_delegations.total.saturating_sub(less); + top_delegations.sort_greatest_to_least(); + true + }; + self.reset_top_data::(candidate.clone(), &top_delegations); + >::insert(candidate, top_delegations); + Ok(in_top_after) + } + /// Decrease bottom delegation + pub fn decrease_bottom_delegation( + &mut self, + candidate: &T::AccountId, + delegator: T::AccountId, + less: BalanceOf, + ) -> Result + where + BalanceOf: Into, + { + let mut bottom_delegations = >::get(candidate) + .expect("CandidateInfo exists => BottomDelegations exists"); + let mut in_bottom = false; + bottom_delegations.delegations = bottom_delegations + .delegations + .clone() + .into_iter() + .map(|d| { + if d.owner != delegator { + d + } else { + in_bottom = true; + Bond { owner: d.owner, amount: d.amount.saturating_sub(less) } + } + }) + .collect(); + ensure!(in_bottom, Error::::DelegationDNE); + bottom_delegations.sort_greatest_to_least(); + self.reset_bottom_data::(&bottom_delegations); + >::insert(candidate, bottom_delegations); + Ok(false) + } +} + +// Temporary manual implementation for migration testing purposes +impl PartialEq for CollatorCandidate { + fn eq(&self, other: &Self) -> bool { + let must_be_true = self.id == other.id && + self.bond == other.bond && + self.total_counted == other.total_counted && + self.total_backing == other.total_backing && + self.request == other.request && + self.state == other.state; + if !must_be_true { + return false; + } + for (x, y) in self.delegators.0.iter().zip(other.delegators.0.iter()) { + if x != y { + return false; + } + } + for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in + self.top_delegations.iter().zip(other.top_delegations.iter()) + { + if o1 != o2 || a1 != a2 { + return false; + } + } + for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in + self.bottom_delegations.iter().zip(other.bottom_delegations.iter()) + { + if o1 != o2 || a1 != a2 { + return false; + } + } + true + } +} + +/// Convey relevant information describing if a delegator was added to the top or bottom +/// Delegations added to the top yield a new total +#[derive(Clone, Copy, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum DelegatorAdded { + AddedToTop { new_total: B }, + AddedToBottom, +} + +impl< + A: Ord + Clone + sp_std::fmt::Debug, + B: AtLeast32BitUnsigned + + Ord + + Copy + + sp_std::ops::AddAssign + + sp_std::ops::SubAssign + + sp_std::fmt::Debug, + > CollatorCandidate +{ + pub fn is_active(&self) -> bool { + self.state == CollatorStatus::Active + } +} + +impl From> for CollatorSnapshot { + fn from(other: CollatorCandidate) -> CollatorSnapshot { + CollatorSnapshot { + bond: other.bond, + delegations: other + .top_delegations + .into_iter() + .map(|d| BondWithAutoCompound { + owner: d.owner, + amount: d.amount, + auto_compound: Percent::zero(), + }) + .collect(), + total: other.total_counted, + } + } +} + +#[allow(deprecated)] +#[derive(Clone, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum DelegatorStatus { + /// Active with no scheduled exit + Active, + /// Schedule exit to revoke all ongoing delegations + #[deprecated(note = "must only be used for backwards compatibility reasons")] + Leaving(RoundIndex), +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +/// Delegator state +pub struct Delegator { + /// Delegator account + pub id: AccountId, + /// All current delegations + pub delegations: OrderedSet>, + /// Total balance locked for this delegator + pub total: Balance, + /// Sum of pending revocation amounts + bond less amounts + pub less_total: Balance, + /// Status for this delegator + pub status: DelegatorStatus, +} + +// Temporary manual implementation for migration testing purposes +impl PartialEq for Delegator { + fn eq(&self, other: &Self) -> bool { + let must_be_true = self.id == other.id && + self.total == other.total && + self.less_total == other.less_total && + self.status == other.status; + if !must_be_true { + return false; + } + for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in + self.delegations.0.iter().zip(other.delegations.0.iter()) + { + if o1 != o2 || a1 != a2 { + return false; + } + } + true + } +} + +impl< + AccountId: Ord + Clone, + Balance: Copy + + sp_std::ops::AddAssign + + sp_std::ops::Add + + sp_std::ops::SubAssign + + sp_std::ops::Sub + + Ord + + Zero + + Default + + Saturating, + > Delegator +{ + pub fn new(id: AccountId, collator: AccountId, amount: Balance) -> Self { + Delegator { + id, + delegations: OrderedSet::from(vec![Bond { owner: collator, amount }]), + total: amount, + less_total: Balance::zero(), + status: DelegatorStatus::Active, + } + } + + pub fn default_with_total(id: AccountId, amount: Balance) -> Self { + Delegator { + id, + total: amount, + delegations: OrderedSet::from(vec![]), + less_total: Balance::zero(), + status: DelegatorStatus::Active, + } + } + + pub fn total(&self) -> Balance { + self.total + } + + pub fn total_sub_if(&mut self, amount: Balance, check: F) -> DispatchResult + where + T: Config, + T::AccountId: From, + BalanceOf: From, + F: Fn(Balance) -> DispatchResult, + { + let total = self.total.saturating_sub(amount); + check(total)?; + self.total = total; + self.adjust_bond_lock::(BondAdjust::Decrease)?; + Ok(()) + } + + pub fn total_add(&mut self, amount: Balance) -> DispatchResult + where + T: Config, + T::AccountId: From, + BalanceOf: From, + { + self.total = self.total.saturating_add(amount); + self.adjust_bond_lock::(BondAdjust::Increase(amount))?; + Ok(()) + } + + pub fn total_sub(&mut self, amount: Balance) -> DispatchResult + where + T: Config, + T::AccountId: From, + BalanceOf: From, + { + self.total = self.total.saturating_sub(amount); + self.adjust_bond_lock::(BondAdjust::Decrease)?; + Ok(()) + } + + pub fn is_active(&self) -> bool { + matches!(self.status, DelegatorStatus::Active) + } + + pub fn add_delegation(&mut self, bond: Bond) -> bool { + let amt = bond.amount; + if self.delegations.insert(bond) { + self.total = self.total.saturating_add(amt); + true + } else { + false + } + } + // Return Some(remaining balance), must be more than MinDelegation + // Return None if delegation not found + pub fn rm_delegation(&mut self, collator: &AccountId) -> Option + where + BalanceOf: From, + T::AccountId: From, + { + let mut amt: Option = None; + let delegations = self + .delegations + .0 + .iter() + .filter_map(|x| { + if &x.owner == collator { + amt = Some(x.amount); + None + } else { + Some(x.clone()) + } + }) + .collect(); + if let Some(balance) = amt { + self.delegations = OrderedSet::from(delegations); + self.total_sub::(balance).expect("Decreasing lock cannot fail, qed"); + Some(self.total) + } else { + None + } + } + + /// Increases the delegation amount and returns `true` if the delegation is part of the + /// TopDelegations set, `false` otherwise. + pub fn increase_delegation( + &mut self, + candidate: AccountId, + amount: Balance, + ) -> Result + where + BalanceOf: From, + T::AccountId: From, + Delegator>: From>, + { + let delegator_id: T::AccountId = self.id.clone().into(); + let candidate_id: T::AccountId = candidate.clone().into(); + let balance_amt: BalanceOf = amount.into(); + // increase delegation + for x in &mut self.delegations.0 { + if x.owner == candidate { + let before_amount: BalanceOf = x.amount.into(); + x.amount = x.amount.saturating_add(amount); + self.total = self.total.saturating_add(amount); + self.adjust_bond_lock::(BondAdjust::Increase(amount))?; + + // update collator state delegation + let mut collator_state = + >::get(&candidate_id).ok_or(Error::::CandidateDNE)?; + let before = collator_state.total_counted; + let in_top = collator_state.increase_delegation::( + &candidate_id, + delegator_id.clone(), + before_amount, + balance_amt, + )?; + let after = collator_state.total_counted; + if collator_state.is_active() && (before != after) { + Pallet::::update_active(candidate_id.clone(), after); + } + >::insert(&candidate_id, collator_state); + let new_total_staked = >::get().saturating_add(balance_amt); + >::put(new_total_staked); + let nom_st: Delegator> = self.clone().into(); + >::insert(&delegator_id, nom_st); + return Ok(in_top); + } + } + Err(Error::::DelegationDNE.into()) + } + + /// Updates the bond locks for this delegator. + /// + /// This will take the current self.total and ensure that a lock of the same amount is applied + /// and when increasing the bond lock will also ensure that the account has enough free balance. + /// + /// `additional_required_balance` should reflect the change to the amount that should be locked + /// if positive, 0 otherwise (e.g. `min(0, change_in_total_bond)`). This is necessary because it + /// is not possible to query the amount that is locked for a given lock id. + pub fn adjust_bond_lock( + &mut self, + additional_required_balance: BondAdjust, + ) -> DispatchResult + where + BalanceOf: From, + T::AccountId: From, + { + match additional_required_balance { + BondAdjust::Increase(amount) => { + ensure!( + >::get_delegator_stakable_free_balance(&self.id.clone().into()) >= + amount.into(), + Error::::InsufficientBalance, + ); + + // additional sanity check: shouldn't ever want to lock more than total + if amount > self.total { + log::warn!("LOGIC ERROR: request to reserve more than bond total"); + return Err(DispatchError::Other("Invalid additional_required_balance")); + } + }, + BondAdjust::Decrease => (), // do nothing on decrease + }; + + if self.total.is_zero() { + T::Currency::remove_lock(DELEGATOR_LOCK_ID, &self.id.clone().into()); + } else { + T::Currency::set_lock( + DELEGATOR_LOCK_ID, + &self.id.clone().into(), + self.total.into(), + WithdrawReasons::all(), + ); + } + Ok(()) + } + + /// Retrieves the bond amount that a delegator has provided towards a collator. + /// Returns `None` if missing. + pub fn get_bond_amount(&self, collator: &AccountId) -> Option { + self.delegations.0.iter().find(|b| &b.owner == collator).map(|b| b.amount) + } +} + +pub mod deprecated { + #![allow(deprecated)] + + use super::*; + + #[deprecated(note = "use DelegationAction")] + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + /// Changes requested by the delegator + /// - limit of 1 ongoing change per delegation + pub enum DelegationChange { + Revoke, + Decrease, + } + + #[deprecated(note = "use ScheduledRequest")] + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + pub struct DelegationRequest { + pub collator: AccountId, + pub amount: Balance, + pub when_executable: RoundIndex, + pub action: DelegationChange, + } + + #[deprecated(note = "use DelegationScheduledRequests storage item")] + #[derive(Clone, Encode, PartialEq, Decode, RuntimeDebug, TypeInfo)] + /// Pending requests to mutate delegations for each delegator + pub struct PendingDelegationRequests { + /// Number of pending revocations (necessary for determining whether revoke is exit) + pub revocations_count: u32, + /// Map from collator -> Request (enforces at most 1 pending request per delegation) + pub requests: BTreeMap>, + /// Sum of pending revocation amounts + bond less amounts + pub less_total: Balance, + } + + impl Default for PendingDelegationRequests { + fn default() -> PendingDelegationRequests { + PendingDelegationRequests { + revocations_count: 0u32, + requests: BTreeMap::new(), + less_total: B::zero(), + } + } + } + + impl< + A: Ord + Clone, + B: Zero + + Ord + + Copy + + Clone + + sp_std::ops::AddAssign + + sp_std::ops::Add + + sp_std::ops::SubAssign + + sp_std::ops::Sub + + Saturating, + > PendingDelegationRequests + { + /// New default (empty) pending requests + pub fn new() -> Self { + Self::default() + } + } + + #[deprecated(note = "use new crate::types::Delegator struct")] + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + /// Delegator state + pub struct Delegator { + /// Delegator account + pub id: AccountId, + /// All current delegations + pub delegations: OrderedSet>, + /// Total balance locked for this delegator + pub total: Balance, + /// Requests to change delegations, relevant iff active + pub requests: PendingDelegationRequests, + /// Status for this delegator + pub status: DelegatorStatus, + } + + // CollatorSnapshot + + #[deprecated(note = "use CollatorSnapshot with BondWithAutoCompound delegations")] + #[derive(Encode, Decode, RuntimeDebug, TypeInfo)] + /// Snapshot of collator state at the start of the round for which they are selected + pub struct CollatorSnapshot { + /// The total value locked by the collator. + pub bond: Balance, + + /// The rewardable delegations. This list is a subset of total delegators, where certain + /// delegators are adjusted based on their scheduled + /// [DelegationChange::Revoke] or [DelegationChange::Decrease] action. + pub delegations: Vec>, + + /// The total counted value locked for the collator, including the self bond + total staked + /// by top delegators. + pub total: Balance, + } + + impl PartialEq for CollatorSnapshot { + fn eq(&self, other: &Self) -> bool { + let must_be_true = self.bond == other.bond && self.total == other.total; + if !must_be_true { + return false; + } + for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in + self.delegations.iter().zip(other.delegations.iter()) + { + if o1 != o2 || a1 != a2 { + return false; + } + } + true + } + } + + impl Default for CollatorSnapshot { + fn default() -> CollatorSnapshot { + CollatorSnapshot { bond: B::default(), delegations: Vec::new(), total: B::default() } + } + } +} + +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +/// DEPRECATED in favor of Delegator +/// Nominator state +pub struct Nominator2 { + /// All current delegations + pub delegations: OrderedSet>, + /// Delegations scheduled to be revoked + pub revocations: OrderedSet, + /// Total balance locked for this nominator + pub total: Balance, + /// Total number of revocations scheduled to be executed + pub scheduled_revocations_count: u32, + /// Total amount to be unbonded once revocations are executed + pub scheduled_revocations_total: Balance, + /// Status for this nominator + pub status: DelegatorStatus, +} + +// /// Temporary function to migrate state +// pub(crate) fn migrate_nominator_to_delegator_state( +// id: T::AccountId, +// nominator: Nominator2>, +// ) -> Delegator> { +// Delegator { +// id, +// delegations: nominator.delegations, +// total: nominator.total, +// requests: PendingDelegationRequests::new(), +// status: nominator.status, +// } +// } + +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +/// The current round index and transition information +pub struct RoundInfo { + /// Current round index + pub current: RoundIndex, + /// The first block of the current round + pub first: BlockNumber, + /// The length of the current round in number of blocks + pub length: u32, +} +impl< + B: Copy + sp_std::ops::Add + sp_std::ops::Sub + From + PartialOrd, + > RoundInfo +{ + pub fn new(current: RoundIndex, first: B, length: u32) -> RoundInfo { + RoundInfo { current, first, length } + } + /// Check if the round should be updated + pub fn should_update(&self, now: B) -> bool { + now - self.first >= self.length.into() + } + /// New round + pub fn update(&mut self, now: B) { + self.current = self.current.saturating_add(1u32); + self.first = now; + } +} +impl< + B: Copy + sp_std::ops::Add + sp_std::ops::Sub + From + PartialOrd, + > Default for RoundInfo +{ + fn default() -> RoundInfo { + RoundInfo::new(1u32, 1u32.into(), 20u32) + } +} + +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +/// Reserve information { account, percent_of_inflation } +pub struct ParachainBondConfig { + /// Account which receives funds intended for parachain bond + pub account: AccountId, + /// Percent of inflation set aside for parachain bond account + pub percent: Percent, +} +impl Default for ParachainBondConfig { + fn default() -> ParachainBondConfig { + ParachainBondConfig { + account: A::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"), + percent: Percent::zero(), + } + } +} + +pub enum BondAdjust { + Increase(Balance), + Decrease, +} diff --git a/pallets/parachain-staking-old/src/weights.rs b/pallets/parachain-staking-old/src/weights.rs new file mode 100644 index 000000000..53d6bbc9c --- /dev/null +++ b/pallets/parachain-staking-old/src/weights.rs @@ -0,0 +1,1853 @@ +// Copyright 2023-2024 Freeverse.io +// This file is part of LAOS. + +// LAOS is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// LAOS is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with LAOS. If not, see . + + +//! Autogenerated weights for `pallet_parachain_staking` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `MacBook-Pro.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` + +// Executed Command: +// ./target/release/laos +// benchmark +// pallet +// --execution=wasm +// --wasm-execution=compiled +// --pallet +// pallet-parachain-staking +// --extrinsic=* +// --steps +// 50 +// --repeat +// 20 +// --template=.maintain/frame-weight-template.hbs +// --json-file +// raw.json +// --output +// ./pallets/parachain-staking/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_parachain_staking`. +pub trait WeightInfo { + fn set_staking_expectations() -> Weight; + fn set_inflation() -> Weight; + fn set_parachain_bond_account() -> Weight; + fn set_parachain_bond_reserve_percent() -> Weight; + fn set_total_selected() -> Weight; + fn set_collator_commission() -> Weight; + fn set_blocks_per_round() -> Weight; + fn join_candidates(x: u32, ) -> Weight; + fn schedule_leave_candidates(x: u32, ) -> Weight; + fn execute_leave_candidates_worst_case(x: u32, ) -> Weight; + fn execute_leave_candidates_ideal(x: u32, y: u32, ) -> Weight; + fn cancel_leave_candidates(x: u32, ) -> Weight; + fn go_offline(x: u32, ) -> Weight; + fn go_online(x: u32, ) -> Weight; + fn candidate_bond_more(x: u32, ) -> Weight; + fn schedule_candidate_bond_less() -> Weight; + fn execute_candidate_bond_less(x: u32, ) -> Weight; + fn set_candidate_bond_to_zero(x: u32, ) -> Weight; + fn cancel_candidate_bond_less() -> Weight; + fn delegate(x: u32, y: u32, ) -> Weight; + fn schedule_revoke_delegation(x: u32, ) -> Weight; + fn delegator_bond_more(x: u32, ) -> Weight; + fn schedule_delegator_bond_less(x: u32, ) -> Weight; + fn execute_revoke_delegation() -> Weight; + fn execute_delegator_revoke_delegation_worst() -> Weight; + fn execute_delegator_bond_less_worst() -> Weight; + fn cancel_delegation_request(x: u32, ) -> Weight; + fn prepare_staking_payouts() -> Weight; + fn get_rewardable_delegators(y: u32, ) -> Weight; + fn select_top_candidates(x: u32, y: u32, ) -> Weight; + fn pay_one_collator_reward_best(x: u32, y: u32, z: u32, ) -> Weight; + fn pay_one_collator_reward(y: u32, ) -> Weight; + fn base_on_initialize() -> Weight; + fn set_auto_compound(x: u32, y: u32, ) -> Weight; + fn delegate_with_auto_compound(x: u32, y: u32, z: u32, ) -> Weight; + fn delegate_with_auto_compound_worst() -> Weight; + fn mint_collator_reward() -> Weight; + fn send_collator_rewards() -> Weight; + fn notify_inactive_collator() -> Weight; +} + +/// Weights for `pallet_parachain_staking` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_staking_expectations() -> Weight { + // Proof Size summary in bytes: + // Measured: `253` + // Estimated: `1738` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(15_000_000, 1738) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_inflation() -> Weight { + // Proof Size summary in bytes: + // Measured: `293` + // Estimated: `1778` + // Minimum execution time: 34_000_000 picoseconds. + Weight::from_parts(35_000_000, 1778) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_parachain_bond_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `210` + // Estimated: `1695` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 1695) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_parachain_bond_reserve_percent() -> Weight { + // Proof Size summary in bytes: + // Measured: `210` + // Estimated: `1695` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 1695) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:1) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_total_selected() -> Weight { + // Proof Size summary in bytes: + // Measured: `205` + // Estimated: `1690` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(15_000_000, 1690) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:1) + /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_collator_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `192` + // Estimated: `1677` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 1677) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::Round` (r:1 w:1) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_blocks_per_round() -> Weight { + // Proof Size summary in bytes: + // Measured: `293` + // Estimated: `1778` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(39_000_000, 1778) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:0 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:0 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn join_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1598 + x * (38 ±0)` + // Estimated: `4783 + x * (41 ±0)` + // Minimum execution time: 60_000_000 picoseconds. + Weight::from_parts(64_274_494, 4783) + // Standard Error: 4_265 + .saturating_add(Weight::from_parts(128_563, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 41).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn schedule_leave_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `980 + x * (37 ±0)` + // Estimated: `4333 + x * (38 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(27_706_760, 4333) + // Standard Error: 2_826 + .saturating_add(Weight::from_parts(86_272, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:350 w:350) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:350 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[2, 350]`. + fn execute_leave_candidates_worst_case(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `751 + x * (431 ±0)` + // Estimated: `5017 + x * (3762 ±0)` + // Minimum execution time: 112_000_000 picoseconds. + Weight::from_parts(116_000_000, 5017) + // Standard Error: 73_097 + .saturating_add(Weight::from_parts(41_339_822, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:350 w:350) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:350 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[2, 350]`. + /// The range of component `y` is `[2, 350]`. + fn execute_leave_candidates_ideal(x: u32, _y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `748 + x * (431 ±0)` + // Estimated: `5017 + x * (3762 ±0)` + // Minimum execution time: 105_000_000 picoseconds. + Weight::from_parts(487_068_098, 5017) + // Standard Error: 122_996 + .saturating_add(Weight::from_parts(43_630_003, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn cancel_leave_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `908 + x * (37 ±0)` + // Estimated: `4261 + x * (38 ±0)` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_369_911, 4261) + // Standard Error: 3_508 + .saturating_add(Weight::from_parts(112_018, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn go_offline(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `805 + x * (38 ±0)` + // Estimated: `4198 + x * (39 ±0)` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(24_752_727, 4198) + // Standard Error: 3_215 + .saturating_add(Weight::from_parts(85_135, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn go_online(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `769 + x * (38 ±0)` + // Estimated: `4162 + x * (39 ±0)` + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(23_558_629, 4162) + // Standard Error: 3_169 + .saturating_add(Weight::from_parts(90_275, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn candidate_bond_more(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1435 + x * (43 ±0)` + // Estimated: `4752 + x * (44 ±0)` + // Minimum execution time: 48_000_000 picoseconds. + Weight::from_parts(58_035_521, 4752) + // Standard Error: 4_449 + .saturating_add(Weight::from_parts(120_440, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn schedule_candidate_bond_less() -> Weight { + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `3913` + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(21_000_000, 3913) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn execute_candidate_bond_less(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1658 + x * (42 ±0)` + // Estimated: `4939 + x * (44 ±0)` + // Minimum execution time: 54_000_000 picoseconds. + Weight::from_parts(66_258_941, 4939) + // Standard Error: 5_111 + .saturating_add(Weight::from_parts(95_631, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn set_candidate_bond_to_zero(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1561 + x * (42 ±0)` + // Estimated: `4842 + x * (44 ±0)` + // Minimum execution time: 48_000_000 picoseconds. + Weight::from_parts(55_481_730, 4842) + // Standard Error: 3_959 + .saturating_add(Weight::from_parts(96_943, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn cancel_candidate_bond_less() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `3893` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3893) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 100]`. + /// The range of component `y` is `[2, 300]`. + fn delegate(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2724 + x * (79 ±0) + y * (38 ±0)` + // Estimated: `5938 + x * (81 ±0) + y * (39 ±0)` + // Minimum execution time: 83_000_000 picoseconds. + Weight::from_parts(83_018_090, 5938) + // Standard Error: 9_947 + .saturating_add(Weight::from_parts(165_371, 0).saturating_mul(x.into())) + // Standard Error: 3_263 + .saturating_add(Weight::from_parts(80_487, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 81).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn schedule_revoke_delegation(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `953 + x * (42 ±0)` + // Estimated: `4396 + x * (43 ±0)` + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(28_036_968, 4396) + // Standard Error: 1_748 + .saturating_add(Weight::from_parts(100_235, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn delegator_bond_more(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2163 + x * (79 ±0)` + // Estimated: `5610 + x * (79 ±0)` + // Minimum execution time: 70_000_000 picoseconds. + Weight::from_parts(81_234_901, 5610) + // Standard Error: 3_223 + .saturating_add(Weight::from_parts(159_034, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 79).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn schedule_delegator_bond_less(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `953 + x * (42 ±0)` + // Estimated: `4396 + x * (43 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(28_726_634, 4396) + // Standard Error: 1_833 + .saturating_add(Weight::from_parts(98_100, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:0) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_revoke_delegation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1221` + // Estimated: `4752` + // Minimum execution time: 93_000_000 picoseconds. + Weight::from_parts(109_000_000, 4752) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_delegator_revoke_delegation_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `37650` + // Estimated: `41115` + // Minimum execution time: 154_000_000 picoseconds. + Weight::from_parts(180_000_000, 41115) + .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_delegator_bond_less_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `30272` + // Estimated: `33737` + // Minimum execution time: 137_000_000 picoseconds. + Weight::from_parts(158_000_000, 33737) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn cancel_delegation_request(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1114 + x * (42 ±0)` + // Estimated: `4543 + x * (43 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(35_122_252, 4543) + // Standard Error: 1_921 + .saturating_add(Weight::from_parts(96_550, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::Points` (r:1 w:0) + /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Staked` (r:1 w:1) + /// Proof: `ParachainStaking::Staked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:0) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:0) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:0) + /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelayedPayouts` (r:0 w:1) + /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn prepare_staking_payouts() -> Weight { + // Proof Size summary in bytes: + // Measured: `594` + // Estimated: `6172` + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(29_000_000, 6172) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:0) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[0, 100]`. + fn get_rewardable_delegators(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `349 + y * (36 ±0)` + // Estimated: `3814 + y * (36 ±0)` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_578_354, 3814) + // Standard Error: 1_539 + .saturating_add(Weight::from_parts(36_261, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:0) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:50 w:0) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:50 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:50 w:0) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:50 w:0) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:0 w:1) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:0 w:50) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 50]`. + /// The range of component `y` is `[0, 100]`. + fn select_top_candidates(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (3817 ±0) + y * (1800 ±0)` + // Estimated: `7646 + x * (3860 ±41) + y * (639 ±20)` + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(11_000_000, 7646) + // Standard Error: 71_042 + .saturating_add(Weight::from_parts(18_570_869, 0).saturating_mul(x.into())) + // Standard Error: 35_426 + .saturating_add(Weight::from_parts(1_053_572, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3860).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 639).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:349 w:349) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:349 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + /// The range of component `y` is `[0, 349]`. + /// The range of component `z` is `[0, 349]`. + fn pay_one_collator_reward_best(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (395 ±0) + y * (156 ±0) + z * (41 ±0)` + // Estimated: `125392 + x * (2591 ±20) + y * (2234 ±20) + z * (28 ±0)` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 125392) + // Standard Error: 650_921 + .saturating_add(Weight::from_parts(91_561_977, 0).saturating_mul(x.into())) + // Standard Error: 650_921 + .saturating_add(Weight::from_parts(38_016_863, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(y.into()))) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(x.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(y.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2234).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 28).saturating_mul(z.into())) + } + /// Storage: `ParachainStaking::DelayedPayouts` (r:1 w:0) + /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Points` (r:1 w:0) + /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:2 w:1) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AwardedPts` (r:1 w:1) + /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:302 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[0, 300]`. + fn pay_one_collator_reward(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `795 + y * (109 ±0)` + // Estimated: `6755 + y * (2591 ±0)` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(67_055_208, 6755) + // Standard Error: 21_113 + .saturating_add(Weight::from_parts(6_363_314, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn base_on_initialize() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `1832` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1832) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 300]`. + /// The range of component `y` is `[0, 100]`. + fn set_auto_compound(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1022 + x * (22 ±0) + y * (36 ±0)` + // Estimated: `4378 + x * (23 ±0) + y * (36 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(29_825_160, 4378) + // Standard Error: 1_769 + .saturating_add(Weight::from_parts(49_729, 0).saturating_mul(x.into())) + // Standard Error: 5_296 + .saturating_add(Weight::from_parts(47_869, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 23).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 350]`. + /// The range of component `y` is `[0, 349]`. + /// The range of component `z` is `[0, 99]`. + fn delegate_with_auto_compound(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (60 ±0) + y * (21 ±0) + z * (78 ±0)` + // Estimated: `26419 + x * (44 ±0) + y * (19 ±0) + z * (76 ±1)` + // Minimum execution time: 90_000_000 picoseconds. + Weight::from_parts(90_069_327, 26419) + // Standard Error: 2_700 + .saturating_add(Weight::from_parts(128_216, 0).saturating_mul(x.into())) + // Standard Error: 2_707 + .saturating_add(Weight::from_parts(7_494, 0).saturating_mul(y.into())) + // Standard Error: 9_535 + .saturating_add(Weight::from_parts(138_309, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 19).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 76).saturating_mul(z.into())) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:2 w:2) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:2 w:2) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:2 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn delegate_with_auto_compound_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `48446` + // Estimated: `54386` + // Minimum execution time: 213_000_000 picoseconds. + Weight::from_parts(242_000_000, 54386) + .saturating_add(T::DbWeight::get().reads(15_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn mint_collator_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `91` + // Estimated: `3581` + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(23_000_000, 3581) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_collator_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `463` + // Estimated: `6172` + // Minimum execution time: 54_000_000 picoseconds. + Weight::from_parts(55_000_000, 6172) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainStaking::EnableMarkingOffline` (r:1 w:0) + /// Proof: `ParachainStaking::EnableMarkingOffline` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:1 w:0) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:1 w:0) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AwardedPts` (r:1 w:0) + /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_inactive_collator() -> Weight { + // Proof Size summary in bytes: + // Measured: `11709` + // Estimated: `15174` + // Minimum execution time: 59_000_000 picoseconds. + Weight::from_parts(74_000_000, 15174) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_staking_expectations() -> Weight { + // Proof Size summary in bytes: + // Measured: `253` + // Estimated: `1738` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(15_000_000, 1738) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_inflation() -> Weight { + // Proof Size summary in bytes: + // Measured: `293` + // Estimated: `1778` + // Minimum execution time: 34_000_000 picoseconds. + Weight::from_parts(35_000_000, 1778) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_parachain_bond_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `210` + // Estimated: `1695` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 1695) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_parachain_bond_reserve_percent() -> Weight { + // Proof Size summary in bytes: + // Measured: `210` + // Estimated: `1695` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 1695) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:1) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_total_selected() -> Weight { + // Proof Size summary in bytes: + // Measured: `205` + // Estimated: `1690` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(15_000_000, 1690) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:1) + /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_collator_commission() -> Weight { + // Proof Size summary in bytes: + // Measured: `192` + // Estimated: `1677` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 1677) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::Round` (r:1 w:1) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_blocks_per_round() -> Weight { + // Proof Size summary in bytes: + // Measured: `293` + // Estimated: `1778` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(39_000_000, 1778) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:0 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:0 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn join_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1598 + x * (38 ±0)` + // Estimated: `4783 + x * (41 ±0)` + // Minimum execution time: 60_000_000 picoseconds. + Weight::from_parts(64_274_494, 4783) + // Standard Error: 4_265 + .saturating_add(Weight::from_parts(128_563, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 41).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn schedule_leave_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `980 + x * (37 ±0)` + // Estimated: `4333 + x * (38 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(27_706_760, 4333) + // Standard Error: 2_826 + .saturating_add(Weight::from_parts(86_272, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:350 w:350) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:350 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[2, 350]`. + fn execute_leave_candidates_worst_case(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `751 + x * (431 ±0)` + // Estimated: `5017 + x * (3762 ±0)` + // Minimum execution time: 112_000_000 picoseconds. + Weight::from_parts(116_000_000, 5017) + // Standard Error: 73_097 + .saturating_add(Weight::from_parts(41_339_822, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:350 w:350) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:350 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[2, 350]`. + /// The range of component `y` is `[2, 350]`. + fn execute_leave_candidates_ideal(x: u32, _y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `748 + x * (431 ±0)` + // Estimated: `5017 + x * (3762 ±0)` + // Minimum execution time: 105_000_000 picoseconds. + Weight::from_parts(487_068_098, 5017) + // Standard Error: 122_996 + .saturating_add(Weight::from_parts(43_630_003, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 200]`. + fn cancel_leave_candidates(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `908 + x * (37 ±0)` + // Estimated: `4261 + x * (38 ±0)` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_369_911, 4261) + // Standard Error: 3_508 + .saturating_add(Weight::from_parts(112_018, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn go_offline(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `805 + x * (38 ±0)` + // Estimated: `4198 + x * (39 ±0)` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(24_752_727, 4198) + // Standard Error: 3_215 + .saturating_add(Weight::from_parts(85_135, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn go_online(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `769 + x * (38 ±0)` + // Estimated: `4162 + x * (39 ±0)` + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(23_558_629, 4162) + // Standard Error: 3_169 + .saturating_add(Weight::from_parts(90_275, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn candidate_bond_more(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1435 + x * (43 ±0)` + // Estimated: `4752 + x * (44 ±0)` + // Minimum execution time: 48_000_000 picoseconds. + Weight::from_parts(58_035_521, 4752) + // Standard Error: 4_449 + .saturating_add(Weight::from_parts(120_440, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn schedule_candidate_bond_less() -> Weight { + // Proof Size summary in bytes: + // Measured: `448` + // Estimated: `3913` + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(21_000_000, 3913) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn execute_candidate_bond_less(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1658 + x * (42 ±0)` + // Estimated: `4939 + x * (44 ±0)` + // Minimum execution time: 54_000_000 picoseconds. + Weight::from_parts(66_258_941, 4939) + // Standard Error: 5_111 + .saturating_add(Weight::from_parts(95_631, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[1, 200]`. + fn set_candidate_bond_to_zero(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1561 + x * (42 ±0)` + // Estimated: `4842 + x * (44 ±0)` + // Minimum execution time: 48_000_000 picoseconds. + Weight::from_parts(55_481_730, 4842) + // Standard Error: 3_959 + .saturating_add(Weight::from_parts(96_943, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn cancel_candidate_bond_less() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `3893` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3893) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[3, 100]`. + /// The range of component `y` is `[2, 300]`. + fn delegate(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2724 + x * (79 ±0) + y * (38 ±0)` + // Estimated: `5938 + x * (81 ±0) + y * (39 ±0)` + // Minimum execution time: 83_000_000 picoseconds. + Weight::from_parts(83_018_090, 5938) + // Standard Error: 9_947 + .saturating_add(Weight::from_parts(165_371, 0).saturating_mul(x.into())) + // Standard Error: 3_263 + .saturating_add(Weight::from_parts(80_487, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 81).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 39).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn schedule_revoke_delegation(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `953 + x * (42 ±0)` + // Estimated: `4396 + x * (43 ±0)` + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(28_036_968, 4396) + // Standard Error: 1_748 + .saturating_add(Weight::from_parts(100_235, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn delegator_bond_more(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2163 + x * (79 ±0)` + // Estimated: `5610 + x * (79 ±0)` + // Minimum execution time: 70_000_000 picoseconds. + Weight::from_parts(81_234_901, 5610) + // Standard Error: 3_223 + .saturating_add(Weight::from_parts(159_034, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(Weight::from_parts(0, 79).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn schedule_delegator_bond_less(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `953 + x * (42 ±0)` + // Estimated: `4396 + x * (43 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(28_726_634, 4396) + // Standard Error: 1_833 + .saturating_add(Weight::from_parts(98_100, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:0) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_revoke_delegation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1221` + // Estimated: `4752` + // Minimum execution time: 93_000_000 picoseconds. + Weight::from_parts(109_000_000, 4752) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_delegator_revoke_delegation_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `37650` + // Estimated: `41115` + // Minimum execution time: 154_000_000 picoseconds. + Weight::from_parts(180_000_000, 41115) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn execute_delegator_bond_less_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `30272` + // Estimated: `33737` + // Minimum execution time: 137_000_000 picoseconds. + Weight::from_parts(158_000_000, 33737) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + fn cancel_delegation_request(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1114 + x * (42 ±0)` + // Estimated: `4543 + x * (43 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(35_122_252, 4543) + // Standard Error: 1_921 + .saturating_add(Weight::from_parts(96_550, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: `ParachainStaking::Points` (r:1 w:0) + /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Staked` (r:1 w:1) + /// Proof: `ParachainStaking::Staked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::InflationConfig` (r:1 w:0) + /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:0) + /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:0) + /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelayedPayouts` (r:0 w:1) + /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn prepare_staking_payouts() -> Weight { + // Proof Size summary in bytes: + // Measured: `594` + // Estimated: `6172` + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(29_000_000, 6172) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:0) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[0, 100]`. + fn get_rewardable_delegators(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `349 + y * (36 ±0)` + // Estimated: `3814 + y * (36 ±0)` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_578_354, 3814) + // Standard Error: 1_539 + .saturating_add(Weight::from_parts(36_261, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:0) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:50 w:0) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:50 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:50 w:0) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:50 w:0) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:0 w:1) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:0 w:50) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 50]`. + /// The range of component `y` is `[0, 100]`. + fn select_top_candidates(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (3817 ±0) + y * (1800 ±0)` + // Estimated: `7646 + x * (3860 ±41) + y * (639 ±20)` + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(11_000_000, 7646) + // Standard Error: 71_042 + .saturating_add(Weight::from_parts(18_570_869, 0).saturating_mul(x.into())) + // Standard Error: 35_426 + .saturating_add(Weight::from_parts(1_053_572, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(x.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(x.into()))) + .saturating_add(Weight::from_parts(0, 3860).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 639).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:350 w:350) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:349 w:349) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:349 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 349]`. + /// The range of component `y` is `[0, 349]`. + /// The range of component `z` is `[0, 349]`. + fn pay_one_collator_reward_best(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (395 ±0) + y * (156 ±0) + z * (41 ±0)` + // Estimated: `125392 + x * (2591 ±20) + y * (2234 ±20) + z * (28 ±0)` + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(0, 125392) + // Standard Error: 650_921 + .saturating_add(Weight::from_parts(91_561_977, 0).saturating_mul(x.into())) + // Standard Error: 650_921 + .saturating_add(Weight::from_parts(38_016_863, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(x.into()))) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(y.into()))) + .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(x.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(y.into()))) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 2234).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 28).saturating_mul(z.into())) + } + /// Storage: `ParachainStaking::DelayedPayouts` (r:1 w:0) + /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Points` (r:1 w:0) + /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:2 w:1) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AwardedPts` (r:1 w:1) + /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:302 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `y` is `[0, 300]`. + fn pay_one_collator_reward(y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `795 + y * (109 ±0)` + // Estimated: `6755 + y * (2591 ±0)` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(67_055_208, 6755) + // Standard Error: 21_113 + .saturating_add(Weight::from_parts(6_363_314, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 2591).saturating_mul(y.into())) + } + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn base_on_initialize() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `1832` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1832) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 300]`. + /// The range of component `y` is `[0, 100]`. + fn set_auto_compound(x: u32, y: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1022 + x * (22 ±0) + y * (36 ±0)` + // Estimated: `4378 + x * (23 ±0) + y * (36 ±0)` + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(29_825_160, 4378) + // Standard Error: 1_769 + .saturating_add(Weight::from_parts(49_729, 0).saturating_mul(x.into())) + // Standard Error: 5_296 + .saturating_add(Weight::from_parts(47_869, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 23).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `x` is `[0, 350]`. + /// The range of component `y` is `[0, 349]`. + /// The range of component `z` is `[0, 99]`. + fn delegate_with_auto_compound(x: u32, y: u32, z: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + x * (60 ±0) + y * (21 ±0) + z * (78 ±0)` + // Estimated: `26419 + x * (44 ±0) + y * (19 ±0) + z * (76 ±1)` + // Minimum execution time: 90_000_000 picoseconds. + Weight::from_parts(90_069_327, 26419) + // Standard Error: 2_700 + .saturating_add(Weight::from_parts(128_216, 0).saturating_mul(x.into())) + // Standard Error: 2_707 + .saturating_add(Weight::from_parts(7_494, 0).saturating_mul(y.into())) + // Standard Error: 9_535 + .saturating_add(Weight::from_parts(138_309, 0).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 19).saturating_mul(y.into())) + .saturating_add(Weight::from_parts(0, 76).saturating_mul(z.into())) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegatorState` (r:2 w:2) + /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) + /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:2 w:2) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:2 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) + /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Total` (r:1 w:1) + /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn delegate_with_auto_compound_worst() -> Weight { + // Proof Size summary in bytes: + // Measured: `48446` + // Estimated: `54386` + // Minimum execution time: 213_000_000 picoseconds. + Weight::from_parts(242_000_000, 54386) + .saturating_add(RocksDbWeight::get().reads(15_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + fn mint_collator_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `91` + // Estimated: `3581` + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(23_000_000, 3581) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) + /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_collator_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `463` + // Estimated: `6172` + // Minimum execution time: 54_000_000 picoseconds. + Weight::from_parts(55_000_000, 6172) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `ParachainStaking::EnableMarkingOffline` (r:1 w:0) + /// Proof: `ParachainStaking::EnableMarkingOffline` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) + /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::SelectedCandidates` (r:1 w:0) + /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::Round` (r:1 w:0) + /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AtStake` (r:1 w:0) + /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::AwardedPts` (r:1 w:0) + /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) + /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) + /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_inactive_collator() -> Weight { + // Proof Size summary in bytes: + // Measured: `11709` + // Estimated: `15174` + // Minimum execution time: 59_000_000 picoseconds. + Weight::from_parts(74_000_000, 15174) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/pallets/parachain-staking/Cargo.toml b/pallets/parachain-staking/Cargo.toml index fc401cf94..34a38a69a 100644 --- a/pallets/parachain-staking/Cargo.toml +++ b/pallets/parachain-staking/Cargo.toml @@ -26,12 +26,11 @@ similar-asserts = { workspace = true } sp-core = { workspace = true, features = [ "std" ] } sp-io = { workspace = true, features = [ "std" ] } sp-runtime = { workspace = true } -test-utils = { workspace = true } [features] default = [ "std" ] std = [ - "frame-benchmarking?/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "parity-scale-codec/std", diff --git a/pallets/parachain-staking/README.md b/pallets/parachain-staking/README.md index 499c9de3d..5d13a7382 100644 --- a/pallets/parachain-staking/README.md +++ b/pallets/parachain-staking/README.md @@ -1,7 +1,5 @@ # DPoS Pallet for Parachain Staking -This pallet is forked from [`moonbeam v0.36.0`](https://github.com/moonbeam-foundation/moonbeam/tree/v0.36.0/pallets/parachain-staking), commit hash `d1087f3091726ffbe14b44655d848d00a1f14201`. - ## Formatting Rules - dependencies in alphabetical order in the `Cargo.toml` and at the top of each file diff --git a/pallets/parachain-staking/migrations.md b/pallets/parachain-staking/migrations.md index 220203115..2c020bebb 100644 --- a/pallets/parachain-staking/migrations.md +++ b/pallets/parachain-staking/migrations.md @@ -1 +1,42 @@ # Migration History + +## Calculate outgoing rewards based on pending revoke and decrease changes + +- [Migration PR `#1408`](https://github.com/moonbeam-foundation/moonbeam/pull/1408) + +## Patch delegations total mismatch + +- [Migration PR `#1291`](https://github.com/moonbeam-foundation/moonbeam/pull/1291) + +## Split candidate state for PoV optimization + +- [Migration PR `#1117`](https://github.com/moonbeam-foundation/moonbeam/pull/1117) + +## Increase max delegations per candidate + +- [Migration PR `#1096`](https://github.com/moonbeam-foundation/moonbeam/pull/1096) +- [Migratio bugfix `#1112`](https://github.com/moonbeam-foundation/moonbeam/pull/1112) + +## Manual Exits and Patch Lack of Delay for bond\_{more, less} + +- [Migration PR `#810`](https://github.com/moonbeam-foundation/moonbeam/pull/810) +- [Migration Removal PR `#?`]() + +## Purge Stale Storage + +- [Migration PR `#970`](https://github.com/moonbeam-foundation/moonbeam/pull/970) + +## Delay nominator exits by changing NominatorState and ExitQueue + +- [Migration PR `#610`](https://github.com/moonbeam-foundation/moonbeam/pull/610) +- [Migration Removal PR `#662`](https://github.com/moonbeam-foundation/moonbeam/pull/662) + +## Patch nomination DOS attack vector by changing CollatorState + +- [Migration PR `#505`](https://github.com/moonbeam-foundation/moonbeam/pull/505) +- [Migration Removal PR `#553`](https://github.com/moonbeam-foundation/moonbeam/pull/553) + +## Patch underflow bug and correct Total storage item + +- [Migration PR `#502`](https://github.com/moonbeam-foundation/moonbeam/pull/502) +- [Migration Removal PR `#553`](https://github.com/moonbeam-foundation/moonbeam/pull/553) diff --git a/pallets/parachain-staking/src/auto_compound.rs b/pallets/parachain-staking/src/auto_compound.rs index 793e70448..e87058971 100644 --- a/pallets/parachain-staking/src/auto_compound.rs +++ b/pallets/parachain-staking/src/auto_compound.rs @@ -1,32 +1,33 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! Auto-compounding functionality for staking rewards -use crate::{ - pallet::{ - AddGet, AutoCompoundingDelegations as AutoCompoundingDelegationsStorage, BalanceOf, - CandidateInfo, Config, DelegatorState, Error, Event, Pallet, Total, - }, - types::{Bond, BondAdjust, Delegator}, +use crate::pallet::{ + AddGet, AutoCompoundingDelegations as AutoCompoundingDelegationsStorage, BalanceOf, + CandidateInfo, Config, DelegatorState, Error, Event, Pallet, Total, }; -use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, traits::Get}; +use crate::types::{Bond, BondAdjust, Delegator}; +use frame_support::dispatch::DispatchResultWithPostInfo; +use frame_support::ensure; +use frame_support::traits::Get; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_runtime::{traits::Saturating, BoundedVec, Percent, RuntimeDebug}; +use sp_runtime::traits::Saturating; +use sp_runtime::{BoundedVec, Percent, RuntimeDebug}; use sp_std::prelude::*; /// Represents the auto-compounding amount for a delegation. @@ -90,31 +91,32 @@ where value: Percent, ) -> Result> { match self.0.binary_search_by(|d| d.delegator.cmp(&delegator)) { - Ok(index) => + Ok(index) => { if self.0[index].value == value { Ok(false) } else { self.0[index].value = value; Ok(true) - }, + } + } Err(index) => { self.0 .try_insert(index, AutoCompoundConfig { delegator, value }) .map_err(|_| Error::::ExceedMaxDelegationsPerDelegator)?; Ok(true) - }, + } } } /// Removes the auto-compounding value for a delegation. - /// Returns `true` if the entry was removed, `false` otherwise. The `delegations_config` must be - /// a sorted vector for binary_search to work. + /// Returns `true` if the entry was removed, `false` otherwise. The `delegations_config` must be a + /// sorted vector for binary_search to work. pub fn remove_for_delegator(&mut self, delegator: &T::AccountId) -> bool { - match self.0.binary_search_by(|d| d.delegator.cmp(delegator)) { + match self.0.binary_search_by(|d| d.delegator.cmp(&delegator)) { Ok(index) => { self.0.remove(index); true - }, + } Err(_) => false, } } @@ -156,10 +158,13 @@ where ) -> DispatchResultWithPostInfo { // check that caller can lock the amount before any changes to storage ensure!( - >::get_delegator_stakable_free_balance(&delegator) >= amount, + >::get_delegator_stakable_balance(&delegator) >= amount, Error::::InsufficientBalance ); - ensure!(amount >= T::MinDelegation::get(), Error::::DelegationBelowMin); + ensure!( + amount >= T::MinDelegation::get(), + Error::::DelegationBelowMin + ); let mut delegator_state = if let Some(mut state) = >::get(&delegator) { // delegation after first @@ -172,13 +177,19 @@ where Error::::ExceedMaxDelegationsPerDelegator ); ensure!( - state.add_delegation(Bond { owner: candidate.clone(), amount }), + state.add_delegation(Bond { + owner: candidate.clone(), + amount + }), Error::::AlreadyDelegatedCandidate ); state } else { // first delegation - ensure!(!>::is_candidate(&delegator), Error::::CandidateExists); + ensure!( + !>::is_candidate(&delegator), + Error::::CandidateExists + ); Delegator::new(delegator.clone(), candidate.clone(), amount) }; let mut candidate_state = @@ -190,29 +201,37 @@ where if !auto_compound.is_zero() { ensure!( - Self::get_auto_compounding_delegation_count(&candidate) as u32 <= - candidate_auto_compounding_delegation_count_hint, + Self::get_auto_compounding_delegation_count(&candidate) as u32 + <= candidate_auto_compounding_delegation_count_hint, >::TooLowCandidateAutoCompoundingDelegationCountToDelegate, ); } // add delegation to candidate - let (delegator_position, less_total_staked) = candidate_state - .add_delegation::(&candidate, Bond { owner: delegator.clone(), amount })?; + let (delegator_position, less_total_staked) = candidate_state.add_delegation::( + &candidate, + Bond { + owner: delegator.clone(), + amount, + }, + )?; // lock delegator amount delegator_state.adjust_bond_lock::(BondAdjust::Increase(amount))?; // adjust total locked, // only is_some if kicked the lowest bottom as a consequence of this new delegation - let net_total_increase = - if let Some(less) = less_total_staked { amount.saturating_sub(less) } else { amount }; + let net_total_increase = if let Some(less) = less_total_staked { + amount.saturating_sub(less) + } else { + amount + }; let new_total_locked = >::get().saturating_add(net_total_increase); // set auto-compound config if the percent is non-zero if !auto_compound.is_zero() { let mut auto_compounding_state = Self::get_storage(&candidate); - auto_compounding_state.set_for_delegator(delegator.clone(), auto_compound)?; + auto_compounding_state.set_for_delegator(delegator.clone(), auto_compound.clone())?; auto_compounding_state.set_storage(&candidate); } @@ -245,7 +264,11 @@ where >::TooLowDelegationCountToAutoCompound, ); ensure!( - delegator_state.delegations.0.iter().any(|b| b.owner == candidate), + delegator_state + .delegations + .0 + .iter() + .any(|b| b.owner == candidate), >::DelegationDNE, ); @@ -263,7 +286,11 @@ where auto_compounding_state.set_storage(&candidate); } - >::deposit_event(Event::AutoCompoundSet { candidate, delegator, value }); + >::deposit_event(Event::AutoCompoundSet { + candidate, + delegator, + value, + }); Ok(().into()) } @@ -273,14 +300,16 @@ where pub(crate) fn remove_auto_compound(candidate: &T::AccountId, delegator: &T::AccountId) { let mut auto_compounding_state = Self::get_storage(candidate); if auto_compounding_state.remove_for_delegator(delegator) { - auto_compounding_state.set_storage(candidate); + auto_compounding_state.set_storage(&candidate); } } /// Returns the value of auto-compound, if it exists for a given delegation, zero otherwise. pub(crate) fn auto_compound(candidate: &T::AccountId, delegator: &T::AccountId) -> Percent { let delegations_config = Self::get_storage(candidate); - delegations_config.get_for_delegator(delegator).unwrap_or_else(Percent::zero) + delegations_config + .get_for_delegator(&delegator) + .unwrap_or_else(|| Percent::zero()) } } @@ -294,10 +323,13 @@ mod tests { let mut delegations_config = AutoCompoundDelegations::::new(vec![].try_into().expect("must succeed")); assert!(delegations_config - .set_for_delegator(1, Percent::from_percent(50)) + .set_for_delegator(1, Percent::from_percent(50)) .expect("must succeed")); assert_eq!( - vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(50) }], + vec![AutoCompoundConfig { + delegator: 1, + value: Percent::from_percent(50), + }], delegations_config.into_inner().into_inner(), ); } @@ -305,15 +337,21 @@ mod tests { #[test] fn test_set_for_delegator_updates_config_and_returns_true_if_entry_changed() { let mut delegations_config = AutoCompoundDelegations::::new( - vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }] - .try_into() - .expect("must succeed"), + vec![AutoCompoundConfig { + delegator: 1, + value: Percent::from_percent(10), + }] + .try_into() + .expect("must succeed"), ); assert!(delegations_config - .set_for_delegator(1, Percent::from_percent(50)) + .set_for_delegator(1, Percent::from_percent(50)) .expect("must succeed")); assert_eq!( - vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(50) }], + vec![AutoCompoundConfig { + delegator: 1, + value: Percent::from_percent(50), + }], delegations_config.into_inner().into_inner(), ); } @@ -321,15 +359,21 @@ mod tests { #[test] fn test_set_for_delegator_updates_config_and_returns_false_if_entry_unchanged() { let mut delegations_config = AutoCompoundDelegations::::new( - vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }] - .try_into() - .expect("must succeed"), + vec![AutoCompoundConfig { + delegator: 1, + value: Percent::from_percent(10), + }] + .try_into() + .expect("must succeed"), ); assert!(!delegations_config - .set_for_delegator(1, Percent::from_percent(10)) + .set_for_delegator(1, Percent::from_percent(10)) .expect("must succeed")); assert_eq!( - vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }], + vec![AutoCompoundConfig { + delegator: 1, + value: Percent::from_percent(10), + }], delegations_config.into_inner().into_inner(), ); } @@ -338,15 +382,18 @@ mod tests { fn test_remove_for_delegator_returns_false_if_entry_was_missing() { let mut delegations_config = AutoCompoundDelegations::::new(vec![].try_into().expect("must succeed")); - assert!(!delegations_config.remove_for_delegator(&1),); + assert_eq!(false, delegations_config.remove_for_delegator(&1),); } #[test] fn test_remove_delegation_config_returns_true_if_entry_existed() { let mut delegations_config = AutoCompoundDelegations::::new( - vec![AutoCompoundConfig { delegator: 1, value: Percent::from_percent(10) }] - .try_into() - .expect("must succeed"), + vec![AutoCompoundConfig { + delegator: 1, + value: Percent::from_percent(10), + }] + .try_into() + .expect("must succeed"), ); assert!(delegations_config.remove_for_delegator(&1)); } diff --git a/pallets/parachain-staking/src/benchmarks.rs b/pallets/parachain-staking/src/benchmarks.rs index 969574f93..074bc3466 100644 --- a/pallets/parachain-staking/src/benchmarks.rs +++ b/pallets/parachain-staking/src/benchmarks.rs @@ -1,18 +1,18 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . #![cfg(feature = "runtime-benchmarks")] @@ -20,7 +20,7 @@ use crate::{ AwardedPts, BalanceOf, BottomDelegations, Call, CandidateBondLessRequest, Config, DelegationAction, EnableMarkingOffline, Pallet, ParachainBondConfig, ParachainBondInfo, Points, - Range, RewardPayment, Round, ScheduledRequest, Staked, TopDelegations, + Range, RewardPayment, Round, ScheduledRequest, TopDelegations, }; use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; use frame_support::traits::{Currency, Get, OnFinalize, OnInitialize}; @@ -50,7 +50,7 @@ fn create_funded_user( let user = account(string, n, SEED); let min_candidate_stk = min_candidate_stk::(); let total = min_candidate_stk + extra; - T::Currency::make_free_balance_be(&user, total); + let _ = T::Currency::make_free_balance_be(&user, total); let _ = T::Currency::issue(total); (user, total) } @@ -65,7 +65,11 @@ fn create_funded_delegator( collator_delegator_count: u32, ) -> Result { let (user, total) = create_funded_user::(string, n, extra); - let bond = if min_bond { min_delegator_stk::() } else { total }; + let bond = if min_bond { + min_delegator_stk::() + } else { + total + }; Pallet::::delegate( RawOrigin::Signed(user.clone()).into(), collator, @@ -145,8 +149,11 @@ fn create_account( 0u32, // first delegation for all calls ) .expect("failed delegating"); - }, - AccountAction::JoinCandidates { amount, candidate_count } => { + } + AccountAction::JoinCandidates { + amount, + candidate_count, + } => { let amount = match amount { Amount::All => initial_balance, Amount::Value(v) => v, @@ -157,7 +164,7 @@ fn create_account( candidate_count, ) .expect("failed joining candidates"); - }, + } }; Ok(acc) @@ -172,8 +179,16 @@ fn create_funded_collator( candidate_count: u32, ) -> Result { let (user, total) = create_funded_user::(string, n, extra); - let bond = if min_bond { min_candidate_stk::() } else { total }; - Pallet::::join_candidates(RawOrigin::Signed(user.clone()).into(), bond, candidate_count)?; + let bond = if min_bond { + min_candidate_stk::() + } else { + total + }; + Pallet::::join_candidates( + RawOrigin::Signed(user.clone()).into(), + bond, + candidate_count, + )?; Ok(user) } @@ -190,7 +205,7 @@ fn roll_to_and_author(round_delay: u32, author: T::AccountId) { let total_rounds = round_delay + 1u32; let round_length: BlockNumberFor = Pallet::::round().length.into(); let mut now = >::block_number() + 1u32.into(); - let first: BlockNumberFor = (Pallet::::round().first as u32).into(); + let first: BlockNumberFor = Pallet::::round().first; let end = first + (round_length * total_rounds.into()); while now < end { parachain_staking_on_finalize::(author.clone()); @@ -288,9 +303,9 @@ benchmarks! { assert_eq!(Pallet::::collator_commission(), Perbill::from_percent(33)); } - set_blocks_per_round {}: _(RawOrigin::Root, 1200u32) + set_blocks_per_round {}: _(RawOrigin::Root, 600u32) verify { - assert_eq!(Pallet::::round().length, 1200u32); + assert_eq!(Pallet::::round().length, 600u32); } // USER DISPATCHABLES @@ -371,7 +386,8 @@ benchmarks! { )?; let mut delegators: Vec = Vec::new(); let mut col_del_count = 0u32; - for (col_del_ac_count, i) in (1..x).enumerate() { + let mut col_del_ac_count = 0u32; + for i in 1..x { let seed = USER_SEED + i; let delegator = create_funded_delegator::( "delegator", @@ -387,9 +403,10 @@ benchmarks! { min_delegator_stk::(), Percent::from_percent(50), col_del_count, - col_del_ac_count as u32, + col_del_ac_count, 1u32, )?; + col_del_ac_count += 1; Pallet::::schedule_revoke_delegation( RawOrigin::Signed(delegator.clone()).into(), @@ -402,7 +419,7 @@ benchmarks! { RawOrigin::Signed(candidate.clone()).into(), 3u32 )?; - roll_to_and_author::(<::LeaveCandidatesDelay as Get>::get(), candidate.clone()); + roll_to_and_author::(T::LeaveCandidatesDelay::get(), candidate.clone()); }: { >::execute_leave_candidates( RawOrigin::Signed(candidate.clone()).into(), @@ -447,9 +464,8 @@ benchmarks! { )?; let mut delegators: Vec = Vec::new(); let mut col_del_count = 0u32; - - #[allow(clippy::explicit_counter_loop)] - for (col_del_ac_count, i) in (1..x).enumerate() { + let mut col_del_ac_count = 0u32; + for i in 1..x { let seed = USER_SEED + i; let delegator = create_funded_delegator::( "delegator", @@ -466,9 +482,10 @@ benchmarks! { min_delegator_stk::(), Percent::from_percent(50), col_del_count, - col_del_ac_count as u32, + col_del_ac_count, 1u32, )?; + col_del_ac_count += 1; } else { Pallet::::delegate( RawOrigin::Signed(delegator.clone()).into(), @@ -490,7 +507,7 @@ benchmarks! { RawOrigin::Signed(candidate.clone()).into(), 3u32 )?; - roll_to_and_author::(<::LeaveCandidatesDelay as Get>::get(), candidate.clone()); + roll_to_and_author::(T::LeaveCandidatesDelay::get(), candidate.clone()); }: { >::execute_leave_candidates_inner(candidate.clone())?; } @@ -648,7 +665,7 @@ benchmarks! { state.request, Some(CandidateBondLessRequest { amount: min_candidate_stk, - when_executable: 1 + <::CandidateBondLessDelay as Get>::get(), + when_executable: T::CandidateBondLessDelay::get() + 1, }) ); } @@ -683,7 +700,7 @@ benchmarks! { RawOrigin::Signed(caller.clone()).into(), min_candidate_stk )?; - roll_to_and_author::(<::LeaveCandidatesDelay as Get>::get(), caller.clone()); + roll_to_and_author::(T::CandidateBondLessDelay::get(), caller.clone()); }: { Pallet::::execute_candidate_bond_less( RawOrigin::Signed(caller.clone()).into(), @@ -778,7 +795,7 @@ benchmarks! { } else { 0u32.into() }; - let (caller, _) = create_funded_user::("caller", USER_SEED, extra); + let (caller, _) = create_funded_user::("caller", USER_SEED, extra.into()); // Delegation count let mut del_del_count = 0u32; // Nominate MaxDelegationsPerDelegators collator candidates @@ -922,7 +939,7 @@ benchmarks! { .cloned(), Some(ScheduledRequest { delegator: last_top_delegator, - when_executable: 1 + <::RevokeDelegationDelay as Get>::get(), + when_executable: 3, action: DelegationAction::Revoke(last_top_delegator_bond), }), ); @@ -1144,7 +1161,7 @@ benchmarks! { .cloned(), Some(ScheduledRequest { delegator: last_top_delegator, - when_executable: 1 + <::RevokeDelegationDelay as Get>::get(), + when_executable: 3, action: DelegationAction::Decrease(bond_less), }), ); @@ -1171,7 +1188,7 @@ benchmarks! { caller.clone()).into(), collator.clone() )?; - roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + roll_to_and_author::(T::RevokeDelegationDelay::get(), collator.clone()); }: { Pallet::::execute_delegation_request( RawOrigin::Signed(caller.clone()).into(), @@ -1293,7 +1310,7 @@ benchmarks! { .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) .unwrap_or_default(), ); - roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + roll_to_and_author::(T::RevokeDelegationDelay::get(), collator.clone()); }: { Pallet::::execute_delegation_request( RawOrigin::Signed(last_top_delegator.clone()).into(), @@ -1354,11 +1371,10 @@ benchmarks! { } let last_top_delegator_bond_less = 1_000u32.into(); - let last_top_delegator_total = decreasing_balance.take(); let last_top_delegator = create_account::( "delegator", seed.take(), - AccountBalance::Value(last_top_delegator_total), + AccountBalance::Value(decreasing_balance.take()), AccountAction::Delegate{ collator: collator.clone(), amount: Amount::All, @@ -1426,7 +1442,10 @@ benchmarks! { .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) .unwrap_or_default(), ); - roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + roll_to_and_author::(T::DelegationBondLessDelay::get(), collator.clone()); + + let last_top_delegator_total = Pallet::::delegator_state(&last_top_delegator) + .expect("could not get delegator state").total; }: { Pallet::::execute_delegation_request( RawOrigin::Signed(last_top_delegator.clone()).into(), @@ -1435,21 +1454,20 @@ benchmarks! { )?; } verify { let expected = last_top_delegator_total - last_top_delegator_bond_less; - // See: https://github.com/freeverseio/laos/pull/533#issuecomment-2034913428 - // assert_eq!( - // Pallet::::delegator_state(&last_top_delegator).expect("candidate was created, qed").total, - // expected, - // ); - // assert!( - // >::get(&collator) - // .map(|bd| bd.delegations.iter().any(|d| d.owner == last_top_delegator)) - // .unwrap_or_default(), - // ); - // assert!( - // >::get(&collator) - // .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) - // .unwrap_or_default(), - // ); + assert_eq!( + Pallet::::delegator_state(&last_top_delegator).expect("could not get delegator state").total, + expected, + ); + assert!( + >::get(&collator) + .map(|bd| bd.delegations.iter().any(|d| d.owner == last_top_delegator)) + .unwrap_or_default(), + ); + assert!( + >::get(&collator) + .map(|bd| bd.delegations.iter().any(|d| d.owner == highest_bottom_delegator)) + .unwrap_or_default(), + ); } cancel_delegation_request { @@ -1508,7 +1526,7 @@ benchmarks! { collator.clone(), 5u32.into(), )?; - roll_to_and_author::(<::RevokeDelegationDelay as Get>::get(), collator.clone()); + roll_to_and_author::(2, collator.clone()); }: { Pallet::::cancel_delegation_request( RawOrigin::Signed(delegator.clone()).into(), @@ -1518,7 +1536,7 @@ benchmarks! { assert!( !Pallet::::delegation_scheduled_requests(&collator) .iter() - .any(|x| x.delegator == delegator) + .any(|x| &x.delegator == &delegator) ); } @@ -1526,15 +1544,19 @@ benchmarks! { prepare_staking_payouts { let reward_delay = <::RewardPaymentDelay as Get>::get(); - let round = reward_delay + 2u32; - let payout_round = round - reward_delay; + let round = crate::RoundInfo { + current: reward_delay + 2u32, + length: 10, + first: 5u32.into(), + first_slot: 5, + }; + let current_slot = 15; + let payout_round = round.current - reward_delay; // may need: // > - // > // > // ensure parachain bond account exists so that deposit_into_existing succeeds >::insert(payout_round, 100); - >::insert(payout_round, min_candidate_stk::()); // set an account in the bond config so that we will measure the payout to it let account = create_funded_user::( @@ -1547,7 +1569,7 @@ benchmarks! { percent: Percent::from_percent(50), }); - }: { Pallet::::prepare_staking_payouts(round); } + }: { Pallet::::prepare_staking_payouts(round, current_slot); } verify { } @@ -1559,7 +1581,7 @@ benchmarks! { ideal: Perbill::one(), max: Perbill::one(), }; - Pallet::::set_inflation(RawOrigin::Root.into(), high_inflation)?; + Pallet::::set_inflation(RawOrigin::Root.into(), high_inflation.clone())?; Pallet::::set_blocks_per_round(RawOrigin::Root.into(), 101u32)?; Pallet::::set_total_selected(RawOrigin::Root.into(), 100u32)?; @@ -1605,7 +1627,7 @@ benchmarks! { ideal: Perbill::one(), max: Perbill::one(), }; - Pallet::::set_inflation(RawOrigin::Root.into(), high_inflation)?; + Pallet::::set_inflation(RawOrigin::Root.into(), high_inflation.clone())?; Pallet::::set_blocks_per_round(RawOrigin::Root.into(), 101u32)?; Pallet::::set_total_selected(RawOrigin::Root.into(), 100u32)?; @@ -1670,8 +1692,9 @@ benchmarks! { )?; let mut delegations = Vec::new(); + let mut col_del_count = 0u32; let initial_delegator_balance = T::MinDelegation::get() + 100u32.into(); - for (col_del_count, i) in (0..x).enumerate() { + for i in 0..x { let auto_compound = if i < y { Percent::from_percent(100) } else { Percent::from_percent(0) }; let delegator = create_account::( "delegator", @@ -1681,10 +1704,11 @@ benchmarks! { collator: prime_candidate.clone(), amount: Amount::All, auto_compound, - collator_delegation_count: col_del_count as u32, - collator_auto_compound_delegation_count: col_del_count as u32, + collator_delegation_count: col_del_count, + collator_auto_compound_delegation_count: col_del_count, }, )?; + col_del_count += 1u32; if i < z { Pallet::::schedule_delegator_bond_less( RawOrigin::Signed(delegator.clone()).into(), @@ -1703,7 +1727,7 @@ benchmarks! { let total_staked = min_candidate_stk::() + (Into::>::into(x) * initial_delegator_balance); let round_for_payout = 5; - >::insert(round_for_payout, DelayedPayout { + >::insert(&round_for_payout, DelayedPayout { round_issuance: 1000u32.into(), total_staking_reward: total_staked, collator_commission: Perbill::from_rational(1u32, 100u32), @@ -1727,7 +1751,7 @@ benchmarks! { { >::mint_and_compound( 100u32.into(), - *auto_compound, + auto_compound.clone(), prime_candidate.clone(), owner.clone(), ); @@ -1741,7 +1765,7 @@ benchmarks! { } in &delegations { assert!( - T::Currency::free_balance(owner) > initial_delegator_balance, + T::Currency::free_balance(&owner) > initial_delegator_balance, "delegator should have been paid in pay_one_collator_reward" ); } @@ -1792,7 +1816,7 @@ benchmarks! { // directly and then call pay_one_collator_reward directly. let round_for_payout = 5; - >::insert(round_for_payout, DelayedPayout { + >::insert(&round_for_payout, DelayedPayout { // NOTE: round_issuance is not correct here, but it doesn't seem to cause problems round_issuance: 1000u32.into(), total_staking_reward: total_staked, @@ -1834,7 +1858,7 @@ benchmarks! { // nominators should have been paid for delegator in &delegators { assert!( - T::Currency::free_balance(delegator) > initial_stake_amount, + T::Currency::free_balance(&delegator) > initial_stake_amount, "delegator should have been paid in pay_one_collator_reward" ); } @@ -2206,7 +2230,7 @@ benchmarks! { )?; let original_free_balance = T::Currency::free_balance(&collator); }: { - Pallet::::mint_collator_reward(1u32, collator.clone(), 50u32.into()) + Pallet::::mint_collator_reward(1u32.into(), collator.clone(), 50u32.into()) } verify { assert_eq!(T::Currency::free_balance(&collator), original_free_balance + 50u32.into()); @@ -2297,13 +2321,16 @@ benchmarks! { #[cfg(test)] mod tests { - use crate::{benchmarks::*, mock::Test}; + use crate::benchmarks::*; + use crate::mock::Test; use frame_support::assert_ok; use sp_io::TestExternalities; use sp_runtime::BuildStorage; pub fn new_test_ext() -> TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); TestExternalities::new(t) } @@ -2483,4 +2510,8 @@ mod tests { } } -impl_benchmark_test_suite!(Pallet, crate::benchmarks::tests::new_test_ext(), crate::mock::Test); +impl_benchmark_test_suite!( + Pallet, + crate::benchmarks::tests::new_test_ext(), + crate::mock::Test +); diff --git a/pallets/parachain-staking/src/delegation_requests.rs b/pallets/parachain-staking/src/delegation_requests.rs index ff7eb4808..ba45b5280 100644 --- a/pallets/parachain-staking/src/delegation_requests.rs +++ b/pallets/parachain-staking/src/delegation_requests.rs @@ -1,36 +1,31 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! Scheduled requests functionality for delegators -use crate::{ - auto_compound::AutoCompoundDelegations, - pallet::{ - BalanceOf, CandidateInfo, Config, DelegationScheduledRequests, DelegatorState, Error, - Event, Pallet, Round, RoundIndex, Total, - }, - weights::WeightInfo, - Delegator, -}; -use frame_support::{ - dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo}, - ensure, - traits::Get, - BoundedVec, +use crate::pallet::{ + BalanceOf, CandidateInfo, Config, DelegationScheduledRequests, DelegatorState, Error, Event, + Pallet, Round, RoundIndex, Total, }; +use crate::weights::WeightInfo; +use crate::{auto_compound::AutoCompoundDelegations, Delegator}; +use frame_support::dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo}; +use frame_support::ensure; +use frame_support::traits::Get; +use frame_support::BoundedVec; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{traits::Saturating, RuntimeDebug}; @@ -87,17 +82,21 @@ impl Pallet { let mut scheduled_requests = >::get(&collator); let actual_weight = - T::WeightInfo::schedule_revoke_delegation(scheduled_requests.len() as u32); + ::WeightInfo::schedule_revoke_delegation(scheduled_requests.len() as u32); ensure!( - !scheduled_requests.iter().any(|req| req.delegator == delegator), + !scheduled_requests + .iter() + .any(|req| req.delegator == delegator), DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: >::PendingDelegationRequestAlreadyExists.into(), }, ); - let bonded_amount = state.get_bond_amount(&collator).ok_or(>::DelegationDNE)?; + let bonded_amount = state + .get_bond_amount(&collator) + .ok_or(>::DelegationDNE)?; let now = >::get().current; let when = now.saturating_add(T::RevokeDelegationDelay::get()); scheduled_requests @@ -132,21 +131,26 @@ impl Pallet { let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; let mut scheduled_requests = >::get(&collator); - let actual_weight = - T::WeightInfo::schedule_delegator_bond_less(scheduled_requests.len() as u32); + let actual_weight = ::WeightInfo::schedule_delegator_bond_less( + scheduled_requests.len() as u32, + ); ensure!( - !scheduled_requests.iter().any(|req| req.delegator == delegator), + !scheduled_requests + .iter() + .any(|req| req.delegator == delegator), DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: >::PendingDelegationRequestAlreadyExists.into(), }, ); - let bonded_amount = state.get_bond_amount(&collator).ok_or(DispatchErrorWithPostInfo { - post_info: Some(actual_weight).into(), - error: >::DelegationDNE.into(), - })?; + let bonded_amount = state + .get_bond_amount(&collator) + .ok_or(DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: >::DelegationDNE.into(), + })?; ensure!( bonded_amount > decrease_amount, DispatchErrorWithPostInfo { @@ -176,7 +180,7 @@ impl Pallet { ); let now = >::get().current; - let when = now.saturating_add(T::RevokeDelegationDelay::get()); + let when = now.saturating_add(T::DelegationBondLessDelay::get()); scheduled_requests .try_push(ScheduledRequest { delegator: delegator.clone(), @@ -208,7 +212,7 @@ impl Pallet { let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; let mut scheduled_requests = >::get(&collator); let actual_weight = - T::WeightInfo::cancel_delegation_request(scheduled_requests.len() as u32); + ::WeightInfo::cancel_delegation_request(scheduled_requests.len() as u32); let request = Self::cancel_request_with_state(&delegator, &mut state, &mut scheduled_requests) @@ -236,7 +240,9 @@ impl Pallet { crate::auto_compound::AddGetOf, >, ) -> Option>> { - let request_idx = scheduled_requests.iter().position(|req| &req.delegator == delegator)?; + let request_idx = scheduled_requests + .iter() + .position(|req| &req.delegator == delegator)?; let request = scheduled_requests.remove(request_idx); let amount = request.action.amount(); @@ -258,11 +264,15 @@ impl Pallet { let request = &scheduled_requests[request_idx]; let now = >::get().current; - ensure!(request.when_executable <= now, >::PendingDelegationRequestNotDueYet); + ensure!( + request.when_executable <= now, + >::PendingDelegationRequestNotDueYet + ); match request.action { DelegationAction::Revoke(amount) => { - let actual_weight = T::WeightInfo::execute_delegator_revoke_delegation_worst(); + let actual_weight = + ::WeightInfo::execute_delegator_revoke_delegation_worst(); // revoking last delegation => leaving set of delegators let leaving = if state.delegations.0.len() == 1usize { @@ -311,9 +321,10 @@ impl Pallet { >::insert(&delegator, state); } Ok(Some(actual_weight).into()) - }, + } DelegationAction::Decrease(_) => { - let actual_weight = T::WeightInfo::execute_delegator_revoke_delegation_worst(); + let actual_weight = + ::WeightInfo::execute_delegator_revoke_delegation_worst(); // remove from pending requests let amount = scheduled_requests.remove(request_idx).action.amount(); @@ -388,7 +399,7 @@ impl Pallet { post_info: Some(actual_weight).into(), error: >::DelegationDNE.into(), }) - }, + } } } @@ -401,8 +412,9 @@ impl Pallet { ) { let mut scheduled_requests = >::get(collator); - let maybe_request_idx = - scheduled_requests.iter().position(|req| &req.delegator == delegator); + let maybe_request_idx = scheduled_requests + .iter() + .position(|req| &req.delegator == delegator); if let Some(request_idx) = maybe_request_idx { let request = scheduled_requests.remove(request_idx); @@ -419,15 +431,16 @@ impl Pallet { .any(|req| &req.delegator == delegator) } - /// Returns true if a [DelegationAction::Revoke] [ScheduledRequest] exists for a given - /// delegation + /// Returns true if a [DelegationAction::Revoke] [ScheduledRequest] exists for a given delegation pub fn delegation_request_revoke_exists( collator: &T::AccountId, delegator: &T::AccountId, ) -> bool { - >::get(collator).iter().any(|req| { - &req.delegator == delegator && matches!(req.action, DelegationAction::Revoke(_)) - }) + >::get(collator) + .iter() + .any(|req| { + &req.delegator == delegator && matches!(req.action, DelegationAction::Revoke(_)) + }) } } @@ -440,7 +453,10 @@ mod tests { fn test_cancel_request_with_state_removes_request_for_correct_delegator_and_updates_state() { let mut state = Delegator { id: 1, - delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + delegations: OrderedSet::from(vec![Bond { + amount: 100, + owner: 2, + }]), total: 100, less_total: 100, status: crate::DelegatorStatus::Active, @@ -482,7 +498,10 @@ mod tests { state, Delegator { id: 1, - delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + delegations: OrderedSet::from(vec![Bond { + amount: 100, + owner: 2, + }]), total: 100, less_total: 0, status: crate::DelegatorStatus::Active, @@ -494,7 +513,10 @@ mod tests { fn test_cancel_request_with_state_does_nothing_when_request_does_not_exist() { let mut state = Delegator { id: 1, - delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + delegations: OrderedSet::from(vec![Bond { + amount: 100, + owner: 2, + }]), total: 100, less_total: 100, status: crate::DelegatorStatus::Active, @@ -522,7 +544,10 @@ mod tests { state, Delegator { id: 1, - delegations: OrderedSet::from(vec![Bond { amount: 100, owner: 2 }]), + delegations: OrderedSet::from(vec![Bond { + amount: 100, + owner: 2, + }]), total: 100, less_total: 100, status: crate::DelegatorStatus::Active, diff --git a/pallets/parachain-staking/src/inflation.rs b/pallets/parachain-staking/src/inflation.rs index 72f1cca01..f8d37d305 100644 --- a/pallets/parachain-staking/src/inflation.rs +++ b/pallets/parachain-staking/src/inflation.rs @@ -1,18 +1,18 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! Helper methods for computing issuance based on inflation use crate::pallet::{BalanceOf, Config, Pallet}; @@ -20,12 +20,18 @@ use frame_support::traits::{Currency, Get}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -use sp_runtime::{PerThing, Perbill, RuntimeDebug}; -use substrate_fixed::{transcendental::pow as floatpow, types::I64F64}; +use sp_runtime::PerThing; +use sp_runtime::{Perbill, RuntimeDebug}; +use substrate_fixed::transcendental::pow as floatpow; +use substrate_fixed::types::I64F64; + +// Milliseconds per year +const MS_PER_YEAR: u64 = 31_557_600_000; fn rounds_per_year() -> u32 { - let blocks_per_round = >::round().length; - T::SlotsPerYear::get() / blocks_per_round + let blocks_per_round = >::round().length as u64; + let blocks_per_year = MS_PER_YEAR / T::BlockTime::get(); + (blocks_per_year / blocks_per_round) as u32 } #[derive( @@ -56,7 +62,11 @@ impl Range { impl From for Range { fn from(other: T) -> Range { - Range { min: other, ideal: other, max: other } + Range { + min: other, + ideal: other, + max: other, + } } } /// Convert an annual inflation to a round inflation @@ -82,32 +92,10 @@ pub fn perbill_annual_to_perbill_round( max: annual_to_round(annual.max), } } - -/// Convert an annual inflation rate to a per-round inflation rate without considering compounding. -/// The calculation is simply dividing the annual rate by the number of rounds per year. -pub fn perbill_annual_to_perbill_round_simple( - annual: Range, - rounds_per_year: u32, -) -> Range { - let annual_to_round_simple = |annual: Perbill| -> Perbill { - // Convert the annual rate from Perbill to a fractional representation. - let x = I64F64::from_num(annual.deconstruct()) / I64F64::from_num(Perbill::ACCURACY); - // Divide the annual rate by the number of rounds to get the per-round rate. - let y: I64F64 = x / I64F64::from_num(rounds_per_year); - // Convert back to Perbill, rounding as necessary. - Perbill::from_parts((y * I64F64::from_num(Perbill::ACCURACY)).floor().to_num::()) - }; - Range { - min: annual_to_round_simple(annual.min), - ideal: annual_to_round_simple(annual.ideal), - max: annual_to_round_simple(annual.max), - } -} - /// Convert annual inflation rate range to round inflation range pub fn annual_to_round(annual: Range) -> Range { let periods = rounds_per_year::(); - perbill_annual_to_perbill_round_simple(annual, periods) + perbill_annual_to_perbill_round(annual, periods) } /// Compute round issuance range from round inflation range and current total issuance @@ -137,7 +125,11 @@ impl InflationInfo { annual: Range, expect: Range, ) -> InflationInfo { - InflationInfo { expect, annual, round: annual_to_round::(annual) } + InflationInfo { + expect, + annual, + round: annual_to_round::(annual), + } } /// Set round inflation range according to input annual inflation range pub fn set_round_from_annual(&mut self, new: Range) { @@ -145,8 +137,8 @@ impl InflationInfo { } /// Reset round inflation rate based on changes to round length pub fn reset_round(&mut self, new_length: u32) { - let periods = T::SlotsPerYear::get() / new_length; - self.round = perbill_annual_to_perbill_round_simple(self.annual, periods); + let periods = (MS_PER_YEAR / T::BlockTime::get()) / (new_length as u64); + self.round = perbill_annual_to_perbill_round(self.annual, periods as u32); } /// Set staking expectations pub fn set_expectations(&mut self, expect: Range) { @@ -177,8 +169,11 @@ mod tests { // 5% inflation for 10_000_0000 = 500,000 minted over the year // let's assume there are 10 periods in a year // => mint 500_000 over 10 periods => 50_000 minted per period - let expected_round_issuance_range: Range = - Range { min: 48_909, ideal: 48_909, max: 48_909 }; + let expected_round_issuance_range: Range = Range { + min: 48_909, + ideal: 48_909, + max: 48_909, + }; let schedule = Range { min: Perbill::from_percent(5), ideal: Perbill::from_percent(5), @@ -194,8 +189,11 @@ mod tests { // 3-5% inflation for 10_000_0000 = 300_000-500,000 minted over the year // let's assume there are 10 periods in a year // => mint 300_000-500_000 over 10 periods => 30_000-50_000 minted per period - let expected_round_issuance_range: Range = - Range { min: 29_603, ideal: 39298, max: 48_909 }; + let expected_round_issuance_range: Range = Range { + min: 29_603, + ideal: 39298, + max: 48_909, + }; let schedule = Range { min: Perbill::from_percent(3), ideal: Perbill::from_percent(4), @@ -208,7 +206,11 @@ mod tests { } #[test] fn expected_parameterization() { - let expected_round_schedule: Range = Range { min: 45, ideal: 56, max: 56 }; + let expected_round_schedule: Range = Range { + min: 45, + ideal: 56, + max: 56, + }; let schedule = Range { min: Perbill::from_percent(4), ideal: Perbill::from_percent(5), @@ -228,23 +230,9 @@ mod tests { }; mock_round_issuance_range(u32::MAX.into(), mock_annual_to_round(schedule, u32::MAX)); mock_round_issuance_range(u64::MAX.into(), mock_annual_to_round(schedule, u32::MAX)); - mock_round_issuance_range(u128::MAX, mock_annual_to_round(schedule, u32::MAX)); + mock_round_issuance_range(u128::MAX.into(), mock_annual_to_round(schedule, u32::MAX)); mock_round_issuance_range(u32::MAX.into(), mock_annual_to_round(schedule, 1)); mock_round_issuance_range(u64::MAX.into(), mock_annual_to_round(schedule, 1)); - mock_round_issuance_range(u128::MAX, mock_annual_to_round(schedule, 1)); - } - - #[test] - fn test_use_simple_interest_per_round_calculation() { - let annual = Range { - min: Perbill::from_parts(75_000_000), - ideal: Perbill::from_parts(75_000_000), - max: Perbill::from_parts(75_000_000), - }; - assert_eq!(perbill_annual_to_perbill_round_simple(annual, 1).ideal.deconstruct(), 74999999); - assert_eq!(perbill_annual_to_perbill_round_simple(annual, 2).ideal.deconstruct(), 37499999); - assert_eq!(perbill_annual_to_perbill_round_simple(annual, 4).ideal.deconstruct(), 18749999); - assert_eq!(perbill_annual_to_perbill_round_simple(annual, 8).ideal.deconstruct(), 9374999); - assert_eq!(perbill_annual_to_perbill_round_simple(annual, 16).ideal.deconstruct(), 4687499); + mock_round_issuance_range(u128::MAX.into(), mock_annual_to_round(schedule, 1)); } } diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 77ff0ce59..36ba65df1 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -1,18 +1,18 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! # Parachain Staking //! Minimal staking pallet that implements collator selection by total backed stake. @@ -25,7 +25,7 @@ //! //! At the start of every round, //! * issuance is calculated for collators (and their delegators) for block authoring -//! `T::RewardPaymentDelay` rounds ago +//! `T::RewardPaymentDelay` rounds ago //! * a new set of collators is chosen from the candidates //! //! Immediately following a round change, payments are made once-per-block until all payments have @@ -78,20 +78,16 @@ pub use RoundIndex; #[pallet] pub mod pallet { - use crate::{ - delegation_requests::{CancelledScheduledRequest, DelegationAction, ScheduledRequest}, - set::BoundedOrderedSet, - traits::*, - types::*, - AddGetOf, AutoCompoundConfig, AutoCompoundDelegations, InflationInfo, Range, WeightInfo, + use crate::delegation_requests::{ + CancelledScheduledRequest, DelegationAction, ScheduledRequest, }; - use frame_support::{ - fail, - pallet_prelude::*, - traits::{ - tokens::WithdrawReasons, Currency, Get, LockIdentifier, LockableCurrency, - ReservableCurrency, - }, + use crate::{set::BoundedOrderedSet, traits::*, types::*,AddGetOf, InflationInfo, Range, WeightInfo}; + use crate::{AutoCompoundConfig, AutoCompoundDelegations}; + use frame_support::fail; + use frame_support::pallet_prelude::*; + use frame_support::traits::{ + tokens::WithdrawReasons, Currency, Get, LockIdentifier, LockableCurrency, + ReservableCurrency, }; use frame_system::pallet_prelude::*; use sp_consensus_slots::Slot; @@ -138,8 +134,8 @@ pub mod pallet { /// Minimum number of blocks per round #[pallet::constant] type MinBlocksPerRound: Get; - /// If a collator doesn't produce any block on this number of rounds, it is notified as - /// inactive. This value must be less than or equal to RewardPaymentDelay. + /// If a collator doesn't produce any block on this number of rounds, it is notified as inactive. + /// This value must be less than or equal to RewardPaymentDelay. #[pallet::constant] type MaxOfflineRounds: Get; /// Number of rounds that candidates remain bonded before exit request is executable @@ -193,16 +189,19 @@ pub mod pallet { /// Handler to notify the runtime when a new round begin. /// If you don't need it, you can specify the type `()`. type OnNewRound: OnNewRound; - /// Get the slot number to use as clocktime for staking rounds + /// Get the current slot number type SlotProvider: Get; - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; + /// Get the slot duration in milliseconds + #[pallet::constant] + type SlotDuration: Get; + /// Get the average time beetween 2 blocks in milliseconds + #[pallet::constant] + type BlockTime: Get; /// Maximum candidates #[pallet::constant] type MaxCandidates: Get; - /// Average number of slots per year - /// A slot here is the unit of time for staking rounds (provided by SlotProvider) - type SlotsPerYear: Get; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } #[pallet::error] @@ -270,7 +269,7 @@ pub mod pallet { pub enum Event { /// Started new round. NewRound { - starting_block: u64, + starting_block: BlockNumberFor, round: RoundIndex, selected_collators_number: u32, total_balance: BalanceOf, @@ -364,7 +363,10 @@ pub mod pallet { scheduled_exit: RoundIndex, }, /// Delegator has left the set of delegators. - DelegatorLeft { delegator: T::AccountId, unstaked_amount: BalanceOf }, + DelegatorLeft { + delegator: T::AccountId, + unstaked_amount: BalanceOf, + }, /// Delegation revoked. DelegationRevoked { delegator: T::AccountId, @@ -401,11 +403,20 @@ pub mod pallet { total_candidate_staked: BalanceOf, }, /// Paid the account (delegator or collator) the balance as liquid rewards. - Rewarded { account: T::AccountId, rewards: BalanceOf }, + Rewarded { + account: T::AccountId, + rewards: BalanceOf, + }, /// Transferred to account which holds funds reserved for parachain bond. - ReservedForParachainBond { account: T::AccountId, value: BalanceOf }, + ReservedForParachainBond { + account: T::AccountId, + value: BalanceOf, + }, /// Account (re)set for parachain bond treasury. - ParachainBondAccountSet { old: T::AccountId, new: T::AccountId }, + ParachainBondAccountSet { + old: T::AccountId, + new: T::AccountId, + }, /// Percent of inflation reserved for parachain bond (re)set. ParachainBondReservePercentSet { old: Percent, new: Percent }, /// Annual inflation input (first 3) was used to derive new per-round inflation (last 3) @@ -430,7 +441,7 @@ pub mod pallet { /// Set blocks per round BlocksPerRoundSet { current_round: RoundIndex, - first_block: u64, + first_block: BlockNumberFor, old: u32, new: u32, new_per_round_inflation_min: Perbill, @@ -438,46 +449,57 @@ pub mod pallet { new_per_round_inflation_max: Perbill, }, /// Auto-compounding reward percent was set for a delegation. - AutoCompoundSet { candidate: T::AccountId, delegator: T::AccountId, value: Percent }, + AutoCompoundSet { + candidate: T::AccountId, + delegator: T::AccountId, + value: Percent, + }, /// Compounded a portion of rewards towards the delegation. - Compounded { candidate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Compounded { + candidate: T::AccountId, + delegator: T::AccountId, + amount: BalanceOf, + }, } #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(_n: BlockNumberFor) -> Weight { - let mut weight = T::WeightInfo::base_on_initialize(); + fn on_initialize(n: BlockNumberFor) -> Weight { + let mut weight = ::WeightInfo::base_on_initialize(); - // fetch slot number - let slot: u64 = T::SlotProvider::get().into(); + let mut round = >::get(); + if round.should_update(n) { + // fetch current slot number + let current_slot: u64 = T::SlotProvider::get().into(); - // account for SlotProvider read - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + // account for SlotProvider read + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 0)); + + // Compute round duration in slots + let round_duration = (current_slot.saturating_sub(round.first_slot)) + .saturating_mul(T::SlotDuration::get()); - let mut round = >::get(); - if round.should_update(slot) { // mutate round - round.update(slot); + round.update(n, current_slot); // notify that new round begin weight = weight.saturating_add(T::OnNewRound::on_new_round(round.current)); // pay all stakers for T::RewardPaymentDelay rounds ago - weight = weight.saturating_add(Self::prepare_staking_payouts(round.current)); + weight = + weight.saturating_add(Self::prepare_staking_payouts(round, round_duration)); // select top collator candidates for next round let (extra_weight, collator_count, _delegation_count, total_staked) = Self::select_top_candidates(round.current); weight = weight.saturating_add(extra_weight); // start next round >::put(round); - // snapshot total stake - >::insert(round.current, >::get()); Self::deposit_event(Event::NewRound { starting_block: round.first, round: round.current, selected_collators_number: collator_count, total_balance: total_staked, }); - // account for Round and Staked writes - weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 2)); + // account for Round write + weight = weight.saturating_add(T::DbWeight::get().reads_writes(0, 1)); } else { weight = weight.saturating_add(Self::handle_delayed_payouts(round.current)); } @@ -512,7 +534,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn round)] /// Current round index and next round scheduled transition - pub type Round = StorageValue<_, RoundInfo, ValueQuery>; + pub type Round = StorageValue<_, RoundInfo>, ValueQuery>; #[pallet::storage] #[pallet::getter(fn delegator_state)] @@ -627,11 +649,6 @@ pub mod pallet { pub type DelayedPayouts = StorageMap<_, Twox64Concat, RoundIndex, DelayedPayout>, OptionQuery>; - #[pallet::storage] - #[pallet::getter(fn staked)] - /// Total counted stake for selected candidates in the round - pub type Staked = StorageMap<_, Twox64Concat, RoundIndex, BalanceOf, ValueQuery>; - #[pallet::storage] #[pallet::getter(fn inflation_config)] /// Inflation configuration @@ -665,14 +682,13 @@ pub mod pallet { #[pallet::getter(fn rewards_account)] pub type RewardsAccount = StorageValue<_, T::AccountId, OptionQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { - /// Initialize balance and register all as collators: `(collator AccountId, balance - /// Amount)` + /// Initialize balance and register all as collators: `(collator AccountId, balance Amount)` pub candidates: Vec<(T::AccountId, BalanceOf)>, /// Initialize balance and make delegations: - /// `(delegator AccountId, collator AccountId, delegation Amount, auto-compounding - /// Percent)` + /// `(delegator AccountId, collator AccountId, delegation Amount, auto-compounding Percent)` pub delegations: Vec>, /// Inflation configuration pub inflation_config: InflationInfo>, @@ -735,15 +751,23 @@ pub mod pallet { // Initialize the delegations for &(ref delegator, ref target, balance, auto_compound) in &self.delegations { assert!( - >::get_delegator_stakable_free_balance(delegator) >= balance, + >::get_delegator_stakable_balance(delegator) >= balance, "Account does not have enough balance to place delegation." ); - let cd_count = - if let Some(x) = col_delegator_count.get(target) { *x } else { 0u32 }; - let dd_count = - if let Some(x) = del_delegation_count.get(delegator) { *x } else { 0u32 }; - let cd_auto_compound_count = - col_auto_compound_delegator_count.get(target).cloned().unwrap_or_default(); + let cd_count = if let Some(x) = col_delegator_count.get(target) { + *x + } else { + 0u32 + }; + let dd_count = if let Some(x) = del_delegation_count.get(delegator) { + *x + } else { + 0u32 + }; + let cd_auto_compound_count = col_auto_compound_delegator_count + .get(target) + .cloned() + .unwrap_or_default(); if let Err(error) = >::delegate_with_auto_compound( T::RuntimeOrigin::from(Some(delegator.clone()).into()), target.clone(), @@ -797,9 +821,9 @@ pub mod pallet { // Choose top TotalSelected collator candidates let (_, v_count, _, total_staked) = >::select_top_candidates(1u32); // Start Round 1 at Block 0 - let round: RoundInfo = RoundInfo::new(1u32, 0u64, self.blocks_per_round); + let round: RoundInfo> = + RoundInfo::new(1u32, Zero::zero(), self.blocks_per_round, 0); >::put(round); - // Set inflation configuration let mut inflation_config = self.inflation_config.clone(); // if all the round values are 0, derive them from the annual values @@ -810,11 +834,8 @@ pub mod pallet { inflation_config.set_round_from_annual::(inflation_config.annual); } >::put(inflation_config); - - // Snapshot total stake - >::insert(1u32, >::get()); >::deposit_event(Event::NewRound { - starting_block: u64::default(), + starting_block: Zero::zero(), round: 1u32, selected_collators_number: v_count, total_balance: total_staked, @@ -835,7 +856,10 @@ pub mod pallet { T::MonetaryGovernanceOrigin::ensure_origin(origin)?; ensure!(expectations.is_valid(), Error::::InvalidSchedule); let mut config = >::get(); - ensure!(config.expect != expectations, Error::::NoWritingSameValue); + ensure!( + config.expect != expectations, + Error::::NoWritingSameValue + ); config.set_expectations(expectations); Self::deposit_event(Event::StakeExpectationsSet { expect_min: config.expect.min, @@ -879,9 +903,15 @@ pub mod pallet { new: T::AccountId, ) -> DispatchResultWithPostInfo { T::MonetaryGovernanceOrigin::ensure_origin(origin)?; - let ParachainBondConfig { account: old, percent } = >::get(); + let ParachainBondConfig { + account: old, + percent, + } = >::get(); ensure!(old != new, Error::::NoWritingSameValue); - >::put(ParachainBondConfig { account: new.clone(), percent }); + >::put(ParachainBondConfig { + account: new.clone(), + percent, + }); Self::deposit_event(Event::ParachainBondAccountSet { old, new }); Ok(().into()) } @@ -894,9 +924,15 @@ pub mod pallet { new: Percent, ) -> DispatchResultWithPostInfo { T::MonetaryGovernanceOrigin::ensure_origin(origin)?; - let ParachainBondConfig { account, percent: old } = >::get(); + let ParachainBondConfig { + account, + percent: old, + } = >::get(); ensure!(old != new, Error::::NoWritingSameValue); - >::put(ParachainBondConfig { account, percent: new }); + >::put(ParachainBondConfig { + account, + percent: new, + }); Self::deposit_event(Event::ParachainBondReservePercentSet { old, new }); Ok(().into()) } @@ -907,8 +943,14 @@ pub mod pallet { #[pallet::weight(::WeightInfo::set_total_selected())] pub fn set_total_selected(origin: OriginFor, new: u32) -> DispatchResultWithPostInfo { frame_system::ensure_root(origin)?; - ensure!(new >= T::MinSelectedCandidates::get(), Error::::CannotSetBelowMin); - ensure!(new <= T::MaxCandidates::get(), Error::::CannotSetAboveMaxCandidates); + ensure!( + new >= T::MinSelectedCandidates::get(), + Error::::CannotSetBelowMin + ); + ensure!( + new <= T::MaxCandidates::get(), + Error::::CannotSetAboveMaxCandidates + ); let old = >::get(); ensure!(old != new, Error::::NoWritingSameValue); ensure!( @@ -943,7 +985,10 @@ pub mod pallet { #[pallet::weight(::WeightInfo::set_blocks_per_round())] pub fn set_blocks_per_round(origin: OriginFor, new: u32) -> DispatchResultWithPostInfo { frame_system::ensure_root(origin)?; - ensure!(new >= T::MinBlocksPerRound::get(), Error::::CannotSetBelowMin); + ensure!( + new >= T::MinBlocksPerRound::get(), + Error::::CannotSetBelowMin + ); let mut round = >::get(); let (now, first, old) = (round.current, round.first, round.length); ensure!(old != new, Error::::NoWritingSameValue); @@ -978,7 +1023,10 @@ pub mod pallet { candidate_count: u32, ) -> DispatchResultWithPostInfo { let acc = ensure_signed(origin)?; - ensure!(bond >= T::MinCandidateStk::get(), Error::::CandidateBondBelowMin); + ensure!( + bond >= T::MinCandidateStk::get(), + Error::::CandidateBondBelowMin + ); Self::join_candidates_inner(acc, bond, candidate_count) } @@ -1048,12 +1096,17 @@ pub mod pallet { Error::::TooLowCandidateCountWeightHintCancelLeaveCandidates ); let maybe_inserted_candidate = candidates - .try_insert(Bond { owner: collator.clone(), amount: state.total_counted }) + .try_insert(Bond { + owner: collator.clone(), + amount: state.total_counted, + }) .map_err(|_| Error::::CandidateLimitReached)?; ensure!(maybe_inserted_candidate, Error::::AlreadyActive); >::put(candidates); >::insert(&collator, state); - Self::deposit_event(Event::CancelledCandidateExit { candidate: collator }); + Self::deposit_event(Event::CancelledCandidateExit { + candidate: collator, + }); Ok(().into()) } @@ -1250,7 +1303,7 @@ pub mod pallet { /// rewards for rounds while the request is pending use the reduced bonded amount. /// A bond less may not be performed if any other scheduled request is pending. #[pallet::call_index(24)] - #[pallet::weight(T::WeightInfo::schedule_delegator_bond_less( + #[pallet::weight(::WeightInfo::schedule_delegator_bond_less( T::MaxTopDelegationsPerCandidate::get() + T::MaxBottomDelegationsPerCandidate::get() ))] pub fn schedule_delegator_bond_less( @@ -1344,7 +1397,10 @@ pub mod pallet { origin: OriginFor, collator: T::AccountId, ) -> DispatchResult { - ensure!(>::get(), >::MarkingOfflineNotEnabled); + ensure!( + >::get(), + >::MarkingOfflineNotEnabled + ); ensure_signed(origin)?; let mut collators_len = 0usize; @@ -1365,7 +1421,10 @@ pub mod pallet { let round_info = >::get(); let max_offline_rounds = T::MaxOfflineRounds::get(); - ensure!(round_info.current > max_offline_rounds, >::CurrentRoundTooLow); + ensure!( + round_info.current > max_offline_rounds, + >::CurrentRoundTooLow + ); // Have rounds_to_check = [8,9] // in case we are in round 10 for instance @@ -1401,7 +1460,7 @@ pub mod pallet { return Err(>::CannotBeNotifiedAsInactive.into()); } - Ok(()) + Ok(().into()) } /// Enable/Disable marking offline feature @@ -1444,7 +1503,8 @@ pub mod pallet { impl Pallet { pub fn set_candidate_bond_to_zero(acc: &T::AccountId) -> Weight { - let actual_weight = T::WeightInfo::set_candidate_bond_to_zero(T::MaxCandidates::get()); + let actual_weight = + ::WeightInfo::set_candidate_bond_to_zero(T::MaxCandidates::get()); if let Some(mut state) = >::get(acc) { state.bond_less::(acc.clone(), state.bond); >::insert(acc, state); @@ -1478,7 +1538,10 @@ pub mod pallet { Error::::TooLowCandidateCountWeightHintJoinCandidates ); let maybe_inserted_candidate = candidates - .try_insert(Bond { owner: acc.clone(), amount: bond }) + .try_insert(Bond { + owner: acc.clone(), + amount: bond, + }) .map_err(|_| Error::::CandidateLimitReached)?; ensure!(maybe_inserted_candidate, Error::::CandidateExists); @@ -1508,7 +1571,7 @@ pub mod pallet { pub fn go_offline_inner(collator: T::AccountId) -> DispatchResultWithPostInfo { let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; let mut candidates = >::get(); - let actual_weight = T::WeightInfo::go_offline(candidates.0.len() as u32); + let actual_weight = ::WeightInfo::go_offline(candidates.0.len() as u32); ensure!( state.is_active(), @@ -1523,14 +1586,16 @@ pub mod pallet { >::put(candidates); } >::insert(&collator, state); - Self::deposit_event(Event::CandidateWentOffline { candidate: collator }); + Self::deposit_event(Event::CandidateWentOffline { + candidate: collator, + }); Ok(Some(actual_weight).into()) } pub fn go_online_inner(collator: T::AccountId) -> DispatchResultWithPostInfo { let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; let mut candidates = >::get(); - let actual_weight = T::WeightInfo::go_online(candidates.0.len() as u32); + let actual_weight = ::WeightInfo::go_online(candidates.0.len() as u32); ensure!( !state.is_active(), @@ -1549,7 +1614,10 @@ pub mod pallet { state.go_online(); let maybe_inserted_candidate = candidates - .try_insert(Bond { owner: collator.clone(), amount: state.total_counted }) + .try_insert(Bond { + owner: collator.clone(), + amount: state.total_counted, + }) .map_err(|_| Error::::CandidateLimitReached)?; ensure!( maybe_inserted_candidate, @@ -1561,7 +1629,9 @@ pub mod pallet { >::put(candidates); >::insert(&collator, state); - Self::deposit_event(Event::CandidateBackOnline { candidate: collator }); + Self::deposit_event(Event::CandidateBackOnline { + candidate: collator, + }); Ok(Some(actual_weight).into()) } @@ -1570,11 +1640,15 @@ pub mod pallet { more: BalanceOf, ) -> DispatchResultWithPostInfo { let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; - let actual_weight = T::WeightInfo::candidate_bond_more(T::MaxCandidates::get()); + let actual_weight = + ::WeightInfo::candidate_bond_more(T::MaxCandidates::get()); - state.bond_more::(collator.clone(), more).map_err(|err| { - DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: err } - })?; + state + .bond_more::(collator.clone(), more) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; let (is_active, total_counted) = (state.is_active(), state.total_counted); >::insert(&collator, state); if is_active { @@ -1587,11 +1661,15 @@ pub mod pallet { candidate: T::AccountId, ) -> DispatchResultWithPostInfo { let mut state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; - let actual_weight = T::WeightInfo::execute_candidate_bond_less(T::MaxCandidates::get()); + let actual_weight = + ::WeightInfo::execute_candidate_bond_less(T::MaxCandidates::get()); - state.execute_bond_less::(candidate.clone()).map_err(|err| { - DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: err } - })?; + state + .execute_bond_less::(candidate.clone()) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; >::insert(&candidate, state); Ok(Some(actual_weight).into()) } @@ -1605,15 +1683,17 @@ pub mod pallet { // TODO use these to return actual weight used via `execute_leave_candidates_ideal` let actual_delegation_count = state.delegation_count; - let actual_weight = T::WeightInfo::execute_leave_candidates_ideal( + let actual_weight = ::WeightInfo::execute_leave_candidates_ideal( actual_delegation_count, actual_auto_compound_delegation_count, ); - state.can_leave::().map_err(|err| DispatchErrorWithPostInfo { - post_info: Some(actual_weight).into(), - error: err, - })?; + state + .can_leave::() + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, + })?; let return_stake = |bond: Bond>| { // remove delegation from delegator state let mut delegator = DelegatorState::::get(&bond.owner).expect( @@ -1678,13 +1758,15 @@ pub mod pallet { Ok(Some(actual_weight).into()) } - /// Returns an account's free balance which is not locked in delegation staking - pub fn get_delegator_stakable_free_balance(acc: &T::AccountId) -> BalanceOf { - let mut balance = T::Currency::free_balance(acc); + /// Returns an account's stakable balance which is not locked in delegation staking + pub fn get_delegator_stakable_balance(acc: &T::AccountId) -> BalanceOf { + let mut stakable_balance = + T::Currency::free_balance(acc).saturating_add(T::Currency::reserved_balance(acc)); + if let Some(state) = >::get(acc) { - balance = balance.saturating_sub(state.total()); + stakable_balance = stakable_balance.saturating_sub(state.total()); } - balance + stakable_balance } /// Returns an account's free balance which is not locked in collator staking @@ -1708,24 +1790,29 @@ pub mod pallet { pub(crate) fn update_active(candidate: T::AccountId, total: BalanceOf) { let mut candidates = >::get(); candidates.remove(&Bond::from_owner(candidate.clone())); - candidates.try_insert(Bond { owner: candidate, amount: total }).expect( - "the candidate is removed in previous step so the length cannot increase; qed", - ); + candidates + .try_insert(Bond { + owner: candidate, + amount: total, + }) + .expect( + "the candidate is removed in previous step so the length cannot increase; qed", + ); >::put(candidates); } - /// Compute round issuance based on total staked for the given round - fn compute_issuance(staked: BalanceOf) -> BalanceOf { + /// Compute round issuance based on duration of the given round + fn compute_issuance(round_duration: u64, round_length: u32) -> BalanceOf { + let ideal_duration: BalanceOf = round_length + .saturating_mul(T::BlockTime::get() as u32) + .into(); let config = >::get(); let round_issuance = crate::inflation::round_issuance_range::(config.round); - // TODO: consider interpolation instead of bounded range - if staked < config.expect.min { - round_issuance.min - } else if staked > config.expect.max { - round_issuance.max - } else { - round_issuance.ideal - } + + // Initial formula: (round_duration / ideal_duration) * ideal_issuance + // We multiply before the division to reduce rounding effects + BalanceOf::::from(round_duration as u32).saturating_mul(round_issuance.ideal) + / (ideal_duration) } /// Remove delegation from candidate state @@ -1750,27 +1837,37 @@ pub mod pallet { Ok(()) } - pub(crate) fn prepare_staking_payouts(now: RoundIndex) -> Weight { - // payout is now - delay rounds ago => now - delay > 0 else return early - let delay = T::RewardPaymentDelay::get(); - if now <= delay { - return Weight::zero(); - } - let round_to_payout = now.saturating_sub(delay); - let total_points = >::get(round_to_payout); - if total_points.is_zero() { + pub(crate) fn prepare_staking_payouts( + round_info: RoundInfo>, + round_duration: u64, + ) -> Weight { + let RoundInfo { + current: now, + length: round_length, + .. + } = round_info; + + // This function is called right after the round index increment, + // and the goal is to compute the payout informations for the round that just ended. + // We don't need to saturate here because the genesis round is 1. + let prepare_payout_for_round = now - 1; + + // Return early if there is no blocks for this round + if >::get(prepare_payout_for_round).is_zero() { return Weight::zero(); } - let total_staked = >::take(round_to_payout); - let total_issuance = Self::compute_issuance(total_staked); - let mut left_issuance = total_issuance; + + // Compute total issuance based on round duration + let total_issuance = Self::compute_issuance(round_duration, round_length); + // reserve portion of issuance for parachain bond account + let mut left_issuance = total_issuance; let bond_config = >::get(); let parachain_bond_reserve = bond_config.percent * total_issuance; if let Ok(amount) = T::PayoutReward::payout(&bond_config.account, parachain_bond_reserve) { - // update round issuance iff transfer succeeds + // update round issuance if transfer succeeds left_issuance = left_issuance.saturating_sub(amount); Self::deposit_event(Event::ReservedForParachainBond { account: bond_config.account, @@ -1784,8 +1881,9 @@ pub mod pallet { collator_commission: >::get(), }; - >::insert(round_to_payout, payout); - T::WeightInfo::prepare_staking_payouts() + >::insert(prepare_payout_for_round, payout); + + ::WeightInfo::prepare_staking_payouts() } /// Wrapper around pay_one_collator_reward which handles the following logic: @@ -1903,7 +2001,12 @@ pub mod pallet { )); // pay delegators due portion - for BondWithAutoCompound { owner, amount, auto_compound } in state.delegations { + for BondWithAutoCompound { + owner, + amount, + auto_compound, + } in state.delegations + { let percent = Perbill::from_rational(amount, state.total); let due = percent * amt_due; if !due.is_zero() { @@ -1911,7 +2014,7 @@ pub mod pallet { num_paid_delegations += 1u32; Self::mint_and_compound( due, - auto_compound, + auto_compound.clone(), collator.clone(), owner.clone(), ); @@ -1919,16 +2022,17 @@ pub mod pallet { } } - extra_weight = - extra_weight.saturating_add(T::WeightInfo::pay_one_collator_reward_best( + extra_weight = extra_weight.saturating_add( + ::WeightInfo::pay_one_collator_reward_best( num_paid_delegations, num_auto_compounding, num_scheduled_requests as u32, - )); + ), + ); ( RewardPayment::Paid, - T::WeightInfo::pay_one_collator_reward(num_delegators as u32) + ::WeightInfo::pay_one_collator_reward(num_delegators as u32) .saturating_add(extra_weight), ) } else { @@ -1958,15 +2062,21 @@ pub mod pallet { let sorted_candidates = candidates .try_mutate(|inner| { inner.select_nth_unstable_by(top_n - 1, |a, b| { - // Order by amount, then owner. The owner is needed to ensure a stable - // order when two accounts have the same amount. - a.amount.cmp(&b.amount).then_with(|| a.owner.cmp(&b.owner)).reverse() + // Order by amount, then owner. The owner is needed to ensure a stable order + // when two accounts have the same amount. + a.amount + .cmp(&b.amount) + .then_with(|| a.owner.cmp(&b.owner)) + .reverse() }); }) .expect("sort cannot increase item count; qed"); - let mut collators = - sorted_candidates.into_iter().take(top_n).map(|x| x.owner).collect::>(); + let mut collators = sorted_candidates + .into_iter() + .take(top_n) + .map(|x| x.owner) + .collect::>(); // Sort collators by AccountId collators.sort(); @@ -2010,7 +2120,7 @@ pub mod pallet { total_exposed_amount: *snapshot_total, }) } - let weight = T::WeightInfo::select_top_candidates(0, 0); + let weight = ::WeightInfo::select_top_candidates(0, 0); return (weight, collator_count, delegation_count, total); } @@ -2022,8 +2132,10 @@ pub mod pallet { collator_count = collator_count.saturating_add(1u32); delegation_count = delegation_count.saturating_add(state.delegation_count); total = total.saturating_add(state.total_counted); - let CountedDelegations { uncounted_stake, rewardable_delegations } = - Self::get_rewardable_delegators(account); + let CountedDelegations { + uncounted_stake, + rewardable_delegations, + } = Self::get_rewardable_delegators(account); let total_counted = state.total_counted.saturating_sub(uncounted_stake); let auto_compounding_delegations = >::get(account) @@ -2061,7 +2173,10 @@ pub mod pallet { ); let avg_delegator_count = delegation_count.checked_div(collator_count).unwrap_or(0); - let weight = T::WeightInfo::select_top_candidates(collator_count, avg_delegator_count); + let weight = ::WeightInfo::select_top_candidates( + collator_count, + avg_delegator_count, + ); (weight, collator_count, delegation_count, total) } @@ -2090,17 +2205,20 @@ pub mod pallet { Some(DelegationAction::Revoke(_)) => { uncounted_stake = uncounted_stake.saturating_add(bond.amount); BalanceOf::::zero() - }, + } Some(DelegationAction::Decrease(amount)) => { uncounted_stake = uncounted_stake.saturating_add(*amount); bond.amount.saturating_sub(*amount) - }, + } }; bond }) .collect(); - CountedDelegations { uncounted_stake, rewardable_delegations } + CountedDelegations { + uncounted_stake, + rewardable_delegations, + } } /// This function exists as a helper to delegator_bond_more & auto_compound functionality. @@ -2122,12 +2240,14 @@ pub mod pallet { Error::::PendingDelegationRevoke ); - let actual_weight = T::WeightInfo::delegator_bond_more( + let actual_weight = ::WeightInfo::delegator_bond_more( >::decode_len(&candidate).unwrap_or_default() as u32, ); - let in_top = - state.increase_delegation::(candidate.clone(), more).map_err(|err| { - DispatchErrorWithPostInfo { post_info: Some(actual_weight).into(), error: err } + let in_top = state + .increase_delegation::(candidate.clone(), more) + .map_err(|err| DispatchErrorWithPostInfo { + post_info: Some(actual_weight).into(), + error: err, })?; Ok((in_top, actual_weight)) diff --git a/pallets/parachain-staking/src/migrations.rs b/pallets/parachain-staking/src/migrations.rs index 19d43bcf5..fb3e89e58 100644 --- a/pallets/parachain-staking/src/migrations.rs +++ b/pallets/parachain-staking/src/migrations.rs @@ -1,17 +1,205 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! # Migrations + +use crate::{types::RoundInfo, Config, RoundIndex}; +use frame_support::pallet_prelude::*; +use frame_support::storage::generator::StorageValue; +use frame_support::storage::unhashed; +use frame_support::traits::OnRuntimeUpgrade; +use frame_system::pallet_prelude::*; +use sp_runtime::Saturating; + +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; + +/// Multiply round length by 2 +pub struct MultiplyRoundLenBy2(core::marker::PhantomData); + +impl OnRuntimeUpgrade for MultiplyRoundLenBy2 +where + T: Config, + BlockNumberFor: From + Into, +{ + fn on_runtime_upgrade() -> frame_support::pallet_prelude::Weight { + let mut round = crate::Round::::get(); + + // Multiply round length by 2 + round.length = round.length * 2; + + crate::Round::::put(round); + + Default::default() + } +} + +/// Migrates RoundInfo and add the field first_slot +pub struct MigrateRoundWithFirstSlot(core::marker::PhantomData); + +#[derive(Decode)] +struct RoundInfoRt2800 { + /// Current round index + pub current: RoundIndex, + /// The first block of the current round + pub first: u64, + /// The length of the current round in number of blocks + pub length: u32, +} +impl> From for RoundInfo { + fn from(round: RoundInfoRt2800) -> Self { + Self { + current: round.current, + first: (round.first as u32).into(), + length: round.length, + first_slot: 0, + } + } +} + +#[derive(Decode)] +struct RoundInfoRt2700 { + /// Current round index + pub current: RoundIndex, + /// The first block of the current round + pub first: u32, + /// The length of the current round in number of blocks + pub length: u32, +} +impl> From for RoundInfo { + fn from(round: RoundInfoRt2700) -> Self { + Self { + current: round.current, + first: round.first.into(), + length: round.length, + first_slot: 0, + } + } +} + +impl OnRuntimeUpgrade for MigrateRoundWithFirstSlot +where + T: Config, + BlockNumberFor: From + Into, +{ + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let raw_key = crate::Round::::storage_value_final_key(); + let maybe_raw_value = unhashed::get_raw(&raw_key); + let len = maybe_raw_value + .expect("ParachainStaking.Round should exist!") + .len(); + ensure!( + len == 12 || len == 16, + "ParachainStaking.Round should have 12 or 16 bytes length!" + ); + + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> frame_support::pallet_prelude::Weight { + let raw_key = crate::Round::::storage_value_final_key(); + + // Read old round info + let mut round: RoundInfo> = if let Some(bytes) = + unhashed::get_raw(&raw_key) + { + let len = bytes.len(); + match len { + // Migration already done + 20 => { + log::info!("MigrateRoundWithFirstSlot already applied."); + return Default::default(); + } + // Migrate from rt2800 + 16 => match RoundInfoRt2800::decode(&mut &bytes[..]) { + Ok(round) => round.into(), + Err(e) => panic!("corrupted storage: fail to decode RoundInfoRt2800: {}", e), + }, + // Migrate from rt2700 + 12 => match RoundInfoRt2700::decode(&mut &bytes[..]) { + Ok(round) => round.into(), + Err(e) => panic!("corrupted storage: fail to decode RoundInfoRt2700: {}", e), + }, + // Storage corrupted + x => panic!( + "corrupted storage: parachainStaking.Round invalid length: {} bytes", + x + ), + } + } else { + panic!("corrupted storage: parachainStaking.Round don't exist"); + }; + + // Compute new field `first_slot`` + round.first_slot = compute_theoretical_first_slot( + >::block_number(), + round.first, + u64::from(T::SlotProvider::get()), + T::BlockTime::get(), + ); + + // Fill DelayedPayouts for rounds N and N-1 + if let Some(delayed_payout) = + >::get(round.current.saturating_sub(2)) + { + >::insert( + round.current.saturating_sub(1), + delayed_payout.clone(), + ); + >::insert(round.current, delayed_payout); + } + + // Apply the migration (write new Round value) + crate::Round::::put(round); + + Default::default() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let _round = crate::Round::::get(); // Should panic if SCALE decode fail + Ok(()) + } +} + +fn compute_theoretical_first_slot>( + current_block: BlockNumber, + first_block: BlockNumber, + current_slot: u64, + block_time: u64, +) -> u64 { + let blocks_since_first: u64 = (current_block.saturating_sub(first_block)).into(); + let slots_since_first = match block_time { + 12_000 => blocks_since_first * 2, + 6_000 => blocks_since_first, + _ => panic!("Unsupported BlockTime"), + }; + current_slot.saturating_sub(slots_since_first) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_compute_theoretical_first_slot() { + assert_eq!( + compute_theoretical_first_slot::(10, 5, 100, 12_000), + 90, + ); + } +} diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index a6b1b0af6..8a72063be 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -1,37 +1,40 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! Test utilities +use crate as pallet_parachain_staking; use crate::{ - self as pallet_parachain_staking, pallet, rewards, AwardedPts, Config, - Event as ParachainStakingEvent, InflationInfo, Points, Range, COLLATOR_LOCK_ID, - DELEGATOR_LOCK_ID, + pallet, AwardedPts, Config, Event as ParachainStakingEvent, InflationInfo, Points, Range, + COLLATOR_LOCK_ID, DELEGATOR_LOCK_ID, }; use block_author::BlockAuthor as BlockAuthorMap; use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{Get, LockIdentifier, OnFinalize, OnInitialize}, + construct_runtime, parameter_types, + traits::{Everything, Get, LockIdentifier, OnFinalize, OnInitialize}, weights::{constants::RocksDbWeight, Weight}, }; use frame_system::pallet_prelude::BlockNumberFor; use sp_consensus_slots::Slot; -use sp_core::ConstU32; -use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill, Percent}; - -pub use test_utils::*; +use sp_core::H256; +use sp_io; +use sp_runtime::BuildStorage; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + Perbill, Percent, +}; pub type AccountId = u64; pub type Balance = u128; @@ -51,41 +54,66 @@ construct_runtime!( ); parameter_types! { + pub const BlockHashCount: u32 = 250; pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); pub const SS58Prefix: u8 = 42; } - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeTask = RuntimeTask; + type Nonce = u64; type Block = Block; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; - type BlockHashCount = ConstU32<250>; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type DbWeight = RocksDbWeight; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + type PreInherents = (); + type PostInherents = (); + type PostTransactions = (); } - parameter_types! { pub const ExistentialDeposit: u128 = 0; } - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; + type WeightInfo = (); type RuntimeHoldReason = (); - type DustRemoval = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeFreezeReason = (); } - impl block_author::Config for Test {} const GENESIS_BLOCKS_PER_ROUND: BlockNumber = 5; const GENESIS_COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20); const GENESIS_PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30); const GENESIS_NUM_SELECTED_CANDIDATES: u32 = 5; - parameter_types! { pub const MinBlocksPerRound: u32 = 3; pub const MaxOfflineRounds: u32 = 1; @@ -102,6 +130,8 @@ parameter_types! { pub const MinCandidateStk: u128 = 10; pub const MinDelegation: u128 = 3; pub const MaxCandidates: u32 = 200; + pub const SlotDuration: u64 = MILLISECS_PER_BLOCK; + pub const BlockTime: u64 = MILLISECS_PER_BLOCK; } pub struct StakingRoundSlotProvider; @@ -132,13 +162,14 @@ impl Config for Test { type MinDelegation = MinDelegation; type BlockAuthor = BlockAuthor; type OnCollatorPayout = (); - type PayoutReward = rewards::TransferFromRewardsAccount; + type PayoutCollatorReward = (); type OnInactiveCollator = (); type OnNewRound = (); type SlotProvider = StakingRoundSlotProvider; type WeightInfo = (); type MaxCandidates = MaxCandidates; - type SlotsPerYear = frame_support::traits::ConstU32<{ 31_557_600 / 6 }>; + type SlotDuration = SlotDuration; + type BlockTime = BlockTime; } pub(crate) struct ExtBuilder { @@ -150,8 +181,6 @@ pub(crate) struct ExtBuilder { delegations: Vec<(AccountId, AccountId, Balance, Percent)>, // inflation config inflation: InflationInfo, - // rewards account balance - rewards_account: Option<(AccountId, Balance)>, } impl Default for ExtBuilder { @@ -161,7 +190,11 @@ impl Default for ExtBuilder { delegations: vec![], collators: vec![], inflation: InflationInfo { - expect: Range { min: 700, ideal: 700, max: 700 }, + expect: Range { + min: 700, + ideal: 700, + max: 700, + }, // not used annual: Range { min: Perbill::from_percent(50), @@ -175,7 +208,6 @@ impl Default for ExtBuilder { max: Perbill::from_percent(5), }, }, - rewards_account: None, } } } @@ -195,8 +227,10 @@ impl ExtBuilder { mut self, delegations: Vec<(AccountId, AccountId, Balance)>, ) -> Self { - self.delegations = - delegations.into_iter().map(|d| (d.0, d.1, d.2, Percent::zero())).collect(); + self.delegations = delegations + .into_iter() + .map(|d| (d.0, d.1, d.2, Percent::zero())) + .collect(); self } @@ -208,11 +242,6 @@ impl ExtBuilder { self } - pub(crate) fn with_rewards_account(mut self, account: AccountId, balance: Balance) -> Self { - self.rewards_account = Some((account, balance)); - self - } - #[allow(dead_code)] pub(crate) fn with_inflation(mut self, inflation: InflationInfo) -> Self { self.inflation = inflation; @@ -224,15 +253,11 @@ impl ExtBuilder { .build_storage() .expect("Frame system builds valid default genesis config"); - let mut rewards_account = None; - let mut balances = self.balances.clone(); - if let Some((account, balance)) = self.rewards_account { - balances.push((account, balance)); - rewards_account = Some(account); + pallet_balances::GenesisConfig:: { + balances: self.balances, } - pallet_balances::GenesisConfig:: { balances } - .assimilate_storage(&mut t) - .expect("Pallet balances storage can be assimilated"); + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); pallet_parachain_staking::GenesisConfig:: { candidates: self.collators, delegations: self.delegations, @@ -241,7 +266,6 @@ impl ExtBuilder { parachain_bond_reserve_percent: GENESIS_PARACHAIN_BOND_RESERVE_PERCENT, blocks_per_round: GENESIS_BLOCKS_PER_ROUND, num_selected_candidates: GENESIS_NUM_SELECTED_CANDIDATES, - rewards_account, } .assimilate_storage(&mut t) .expect("Parachain Staking's storage can be assimilated"); @@ -252,13 +276,24 @@ impl ExtBuilder { } } +/// Rolls forward one block. Returns the new block number. +fn roll_one_block() -> BlockNumber { + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::reset_events(); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + ParachainStaking::on_initialize(System::block_number()); + System::block_number() +} + /// Rolls to the desired block. Returns the number of blocks played. pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber { let mut num_blocks = 0; let mut block = System::block_number(); while block < n { - roll_one_block!(true); - block = System::block_number(); + block = roll_one_block(); num_blocks += 1; } num_blocks @@ -266,10 +301,11 @@ pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber { /// Rolls desired number of blocks. Returns the final block. pub(crate) fn roll_blocks(num_blocks: u32) -> BlockNumber { + let mut block = System::block_number(); for _ in 0..num_blocks { - roll_one_block!(true); + block = roll_one_block(); } - System::block_number() + block } /// Rolls block-by-block to the beginning of the specified round. @@ -291,18 +327,210 @@ pub(crate) fn events() -> Vec> { System::events() .into_iter() .map(|r| r.event) - .filter_map( - |e| { - if let RuntimeEvent::ParachainStaking(inner) = e { - Some(inner) - } else { - None - } - }, - ) + .filter_map(|e| { + if let RuntimeEvent::ParachainStaking(inner) = e { + Some(inner) + } else { + None + } + }) .collect::>() } +/// Asserts that some events were never emitted. +/// +/// # Example +/// +/// ``` +/// assert_no_events!(); +/// ``` +#[macro_export] +macro_rules! assert_no_events { + () => { + similar_asserts::assert_eq!(Vec::>::new(), crate::mock::events()) + }; +} + +/// Asserts that emitted events match exactly the given input. +/// +/// # Example +/// +/// ``` +/// assert_events_eq!( +/// Foo { x: 1, y: 2 }, +/// Bar { value: "test" }, +/// Baz { a: 10, b: 20 }, +/// ); +/// ``` +#[macro_export] +macro_rules! assert_events_eq { + ($event:expr) => { + similar_asserts::assert_eq!(vec![$event], crate::mock::events()); + }; + ($($events:expr,)+) => { + similar_asserts::assert_eq!(vec![$($events,)+], crate::mock::events()); + }; +} + +/// Asserts that some emitted events match the given input. +/// +/// # Example +/// +/// ``` +/// assert_events_emitted!( +/// Foo { x: 1, y: 2 }, +/// Baz { a: 10, b: 20 }, +/// ); +/// ``` +#[macro_export] +macro_rules! assert_events_emitted { + ($event:expr) => { + [$event].into_iter().for_each(|e| assert!( + crate::mock::events().into_iter().find(|x| x == &e).is_some(), + "Event {:?} was not found in events: \n{:#?}", + e, + crate::mock::events() + )); + }; + ($($events:expr,)+) => { + [$($events,)+].into_iter().for_each(|e| assert!( + crate::mock::events().into_iter().find(|x| x == &e).is_some(), + "Event {:?} was not found in events: \n{:#?}", + e, + crate::mock::events() + )); + }; +} + +/// Asserts that some events were never emitted. +/// +/// # Example +/// +/// ``` +/// assert_events_not_emitted!( +/// Foo { x: 1, y: 2 }, +/// Bar { value: "test" }, +/// ); +/// ``` +#[macro_export] +macro_rules! assert_events_not_emitted { + ($event:expr) => { + [$event].into_iter().for_each(|e| assert!( + crate::mock::events().into_iter().find(|x| x != &e).is_some(), + "Event {:?} was unexpectedly found in events: \n{:#?}", + e, + crate::mock::events() + )); + }; + ($($events:expr,)+) => { + [$($events,)+].into_iter().for_each(|e| assert!( + crate::mock::events().into_iter().find(|x| x != &e).is_some(), + "Event {:?} was unexpectedly found in events: \n{:#?}", + e, + crate::mock::events() + )); + }; +} + +/// Asserts that the emitted events are exactly equal to the input patterns. +/// +/// # Example +/// +/// ``` +/// assert_events_eq_match!( +/// Foo { x: 1, .. }, +/// Bar { .. }, +/// Baz { a: 10, b: 20 }, +/// ); +/// ``` +#[macro_export] +macro_rules! assert_events_eq_match { + ($index:expr;) => { + assert_eq!( + $index, + crate::mock::events().len(), + "Found {} extra event(s): \n{:#?}", + crate::mock::events().len()-$index, + crate::mock::events() + ); + }; + ($index:expr; $event:pat_param, $($events:pat_param,)*) => { + assert!( + matches!( + crate::mock::events().get($index), + Some($event), + ), + "Event {:#?} was not found at index {}: \n{:#?}", + stringify!($event), + $index, + crate::mock::events() + ); + assert_events_eq_match!($index+1; $($events,)*); + }; + ($event:pat_param) => { + assert_events_eq_match!(0; $event,); + }; + ($($events:pat_param,)+) => { + assert_events_eq_match!(0; $($events,)+); + }; +} + +/// Asserts that some emitted events match the input patterns. +/// +/// # Example +/// +/// ``` +/// assert_events_emitted_match!( +/// Foo { x: 1, .. }, +/// Baz { a: 10, b: 20 }, +/// ); +/// ``` +#[macro_export] +macro_rules! assert_events_emitted_match { + ($event:pat_param) => { + assert!( + crate::mock::events().into_iter().any(|x| matches!(x, $event)), + "Event {:?} was not found in events: \n{:#?}", + stringify!($event), + crate::mock::events() + ); + }; + ($event:pat_param, $($events:pat_param,)+) => { + assert_events_emitted_match!($event); + $( + assert_events_emitted_match!($events); + )+ + }; +} + +/// Asserts that the input patterns match none of the emitted events. +/// +/// # Example +/// +/// ``` +/// assert_events_not_emitted_match!( +/// Foo { x: 1, .. }, +/// Baz { a: 10, b: 20 }, +/// ); +/// ``` +#[macro_export] +macro_rules! assert_events_not_emitted_match { + ($event:pat_param) => { + assert!( + crate::mock::events().into_iter().any(|x| !matches!(x, $event)), + "Event {:?} was unexpectedly found in events: \n{:#?}", + stringify!($event), + crate::mock::events() + ); + }; + ($event:pat_param, $($events:pat_param,)+) => { + assert_events_not_emitted_match!($event); + $( + assert_events_not_emitted_match!($events); + )+ + }; +} + // Same storage changes as ParachainStaking::on_finalize pub(crate) fn set_author(round: BlockNumber, acc: u64, pts: u32) { >::mutate(round, |p| *p += pts); @@ -316,7 +544,7 @@ pub(crate) fn set_block_author(acc: u64) { /// fn to query the lock amount pub(crate) fn query_lock_amount(account_id: u64, id: LockIdentifier) -> Option { - for lock in Balances::locks(account_id) { + for lock in Balances::locks(&account_id) { if lock.id == id { return Some(lock.amount); } @@ -344,16 +572,22 @@ fn geneses() { .execute_with(|| { assert!(System::events().is_empty()); // collators - assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 500); + assert_eq!( + ParachainStaking::get_collator_stakable_free_balance(&1), + 500 + ); assert_eq!(query_lock_amount(1, COLLATOR_LOCK_ID), Some(500)); assert!(ParachainStaking::is_candidate(&1)); assert_eq!(query_lock_amount(2, COLLATOR_LOCK_ID), Some(200)); - assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&2), 100); + assert_eq!( + ParachainStaking::get_collator_stakable_free_balance(&2), + 100 + ); assert!(ParachainStaking::is_candidate(&2)); // delegators for x in 3..7 { assert!(ParachainStaking::is_delegator(&x)); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&x), 0); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&x), 0); assert_eq!(query_lock_amount(x, DELEGATOR_LOCK_ID), Some(100)); } // uninvolved @@ -362,13 +596,16 @@ fn geneses() { } // no delegator staking locks assert_eq!(query_lock_amount(7, DELEGATOR_LOCK_ID), None); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&7), 100); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&7), 100); assert_eq!(query_lock_amount(8, DELEGATOR_LOCK_ID), None); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&8), 9); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&8), 9); assert_eq!(query_lock_amount(9, DELEGATOR_LOCK_ID), None); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&9), 4); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&9), 4); // no collator staking locks - assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&7), 100); + assert_eq!( + ParachainStaking::get_collator_stakable_free_balance(&7), + 100 + ); assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&8), 9); assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&9), 4); }); @@ -386,7 +623,13 @@ fn geneses() { (10, 100), ]) .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) - .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .with_delegations(vec![ + (6, 1, 10), + (7, 1, 10), + (8, 2, 10), + (9, 2, 10), + (10, 1, 10), + ]) .build() .execute_with(|| { assert!(System::events().is_empty()); @@ -403,16 +646,16 @@ fn geneses() { for x in 6..11 { assert!(ParachainStaking::is_delegator(&x)); assert_eq!(query_lock_amount(x, DELEGATOR_LOCK_ID), Some(10)); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&x), 90); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&x), 90); } }); } -#[allow(unused_imports)] #[frame_support::pallet] -pub(crate) mod block_author { +pub mod block_author { use super::*; - use frame_support::{pallet_prelude::*, traits::Get}; + use frame_support::pallet_prelude::*; + use frame_support::traits::Get; #[pallet::config] pub trait Config: frame_system::Config {} @@ -511,8 +754,14 @@ fn test_assert_events_eq_fails_if_event_extra() { selected_collators_number: 1, total_balance: 10, }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 200 }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 200, + }, ); }); } @@ -524,7 +773,10 @@ fn test_assert_events_eq_fails_if_event_wrong_order() { inject_test_events(); assert_events_eq!( - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, ParachainStakingEvent::CollatorChosen { round: 2, collator_account: 1, @@ -558,7 +810,10 @@ fn test_assert_events_eq_fails_if_event_wrong_value() { selected_collators_number: 1, total_balance: 10, }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 50 }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 50, + }, ); }); } @@ -566,9 +821,15 @@ fn test_assert_events_eq_fails_if_event_wrong_value() { #[test] fn test_assert_events_eq_passes_if_all_events_present_single() { ExtBuilder::default().build().execute_with(|| { - System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + System::deposit_event(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }); - assert_events_eq!(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + assert_events_eq!(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }); }); } @@ -589,7 +850,10 @@ fn test_assert_events_eq_passes_if_all_events_present_multiple() { selected_collators_number: 1, total_balance: 10, }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, ); }); } @@ -614,16 +878,25 @@ fn test_assert_events_emitted_fails_if_event_wrong_value() { ExtBuilder::default().build().execute_with(|| { inject_test_events(); - assert_events_emitted!(ParachainStakingEvent::Rewarded { account: 1, rewards: 50 }); + assert_events_emitted!(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 50, + }); }); } #[test] fn test_assert_events_emitted_passes_if_all_events_present_single() { ExtBuilder::default().build().execute_with(|| { - System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + System::deposit_event(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }); - assert_events_emitted!(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + assert_events_emitted!(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }); }); } @@ -638,7 +911,10 @@ fn test_assert_events_emitted_passes_if_all_events_present_multiple() { collator_account: 1, total_exposed_amount: 10, }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, ); }); } @@ -702,7 +978,10 @@ fn test_assert_events_eq_match_fails_if_event_wrong_value() { #[test] fn test_assert_events_eq_match_passes_if_all_events_present_single() { ExtBuilder::default().build().execute_with(|| { - System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + System::deposit_event(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }); assert_events_eq_match!(ParachainStakingEvent::Rewarded { account: 1, .. }); }); @@ -714,9 +993,19 @@ fn test_assert_events_eq_match_passes_if_all_events_present_multiple() { inject_test_events(); assert_events_eq_match!( - ParachainStakingEvent::CollatorChosen { round: 2, collator_account: 1, .. }, - ParachainStakingEvent::NewRound { starting_block: 10, .. }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::CollatorChosen { + round: 2, + collator_account: 1, + .. + }, + ParachainStakingEvent::NewRound { + starting_block: 10, + .. + }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, ); }); } @@ -747,7 +1036,10 @@ fn test_assert_events_emitted_match_fails_if_event_wrong_value() { #[test] fn test_assert_events_emitted_match_passes_if_all_events_present_single() { ExtBuilder::default().build().execute_with(|| { - System::deposit_event(ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }); + System::deposit_event(ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }); assert_events_emitted_match!(ParachainStakingEvent::Rewarded { rewards: 100, .. }); }); @@ -759,8 +1051,14 @@ fn test_assert_events_emitted_match_passes_if_all_events_present_multiple() { inject_test_events(); assert_events_emitted_match!( - ParachainStakingEvent::CollatorChosen { total_exposed_amount: 10, .. }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::CollatorChosen { + total_exposed_amount: 10, + .. + }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, ); }); } @@ -778,7 +1076,10 @@ fn inject_test_events() { selected_collators_number: 1, total_balance: 10, }, - ParachainStakingEvent::Rewarded { account: 1, rewards: 100 }, + ParachainStakingEvent::Rewarded { + account: 1, + rewards: 100, + }, ] .into_iter() .for_each(System::deposit_event); diff --git a/pallets/parachain-staking/src/set.rs b/pallets/parachain-staking/src/set.rs index 9cae12f71..2fb15ffad 100644 --- a/pallets/parachain-staking/src/set.rs +++ b/pallets/parachain-staking/src/set.rs @@ -1,20 +1,20 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . -/* TODO: use orml_utilities::OrderedSet without leaking substrate v2.0 dependencies */ +/* TODO: use orml_utilities::OrderedSet without leaking substrate v2.0 dependencies*/ use frame_support::traits::Get; use parity_scale_codec::{Decode, Encode}; @@ -57,7 +57,7 @@ impl OrderedSet { Err(loc) => { self.0.insert(loc, value); true - }, + } } } @@ -68,7 +68,7 @@ impl OrderedSet { Ok(loc) => { self.0.remove(loc); true - }, + } Err(_) => false, } } @@ -140,7 +140,7 @@ impl> BoundedOrderedSet { Ok(loc) => { self.0.remove(loc); true - }, + } Err(_) => false, } } diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index 194cc077b..0d6ffa635 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -1,18 +1,18 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! # Staking Pallet Unit Tests //! The unit tests are organized by the call they test. The order matches the order @@ -22,16 +22,16 @@ //! 3. Public (Collator, Nominator) //! 4. Miscellaneous Property-Based Tests +use crate::auto_compound::{AutoCompoundConfig, AutoCompoundDelegations}; +use crate::delegation_requests::{CancelledScheduledRequest, DelegationAction, ScheduledRequest}; +use crate::mock::{ + roll_blocks, roll_to, roll_to_round_begin, roll_to_round_end, set_author, set_block_author, + Balances, BlockNumber, ExtBuilder, ParachainStaking, RuntimeOrigin, Test, +}; use crate::{ - auto_compound::{AutoCompoundConfig, AutoCompoundDelegations}, - delegation_requests::{CancelledScheduledRequest, DelegationAction, ScheduledRequest}, - mock::{ - self, assert_events_emitted, assert_events_emitted_match, assert_events_eq, - assert_no_events, roll_blocks, roll_to, roll_to_round_begin, roll_to_round_end, set_author, - set_block_author, Balances, BlockNumber, ExtBuilder, ParachainStaking, RuntimeOrigin, Test, - }, + assert_events_emitted, assert_events_emitted_match, assert_events_eq, assert_no_events, AtStake, Bond, CollatorStatus, DelegationScheduledRequests, DelegatorAdded, - EnableMarkingOffline, Error, Event, InflationInfo, Range, DELEGATOR_LOCK_ID, + EnableMarkingOffline, Error, Event, Range, DELEGATOR_LOCK_ID, }; use frame_support::{assert_err, assert_noop, assert_ok, pallet_prelude::*, BoundedVec}; use sp_runtime::{traits::Zero, DispatchError, ModuleError, Perbill, Percent}; @@ -65,10 +65,19 @@ fn invalid_root_origin_fails() { fn set_total_selected_event_emits_correctly() { ExtBuilder::default().build().execute_with(|| { // before we can bump total_selected we must bump the blocks per round - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 7u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 7u32 + )); roll_blocks(1); - assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 6u32)); - assert_events_eq!(Event::TotalSelectedSet { old: 5u32, new: 6u32 }); + assert_ok!(ParachainStaking::set_total_selected( + RuntimeOrigin::root(), + 6u32 + )); + assert_events_eq!(Event::TotalSelectedSet { + old: 5u32, + new: 6u32 + }); }); } @@ -97,7 +106,10 @@ fn set_total_selected_fails_if_above_max_candidates() { #[test] fn set_total_selected_fails_if_equal_to_blocks_per_round() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 10u32 + )); assert_noop!( ParachainStaking::set_total_selected(RuntimeOrigin::root(), 10u32), Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, @@ -108,16 +120,28 @@ fn set_total_selected_fails_if_equal_to_blocks_per_round() { #[test] fn set_total_selected_passes_if_below_blocks_per_round() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); - assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 9u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 10u32 + )); + assert_ok!(ParachainStaking::set_total_selected( + RuntimeOrigin::root(), + 9u32 + )); }); } #[test] fn set_blocks_per_round_fails_if_below_total_selected() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 20u32)); - assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 10u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 20u32 + )); + assert_ok!(ParachainStaking::set_total_selected( + RuntimeOrigin::root(), + 10u32 + )); assert_noop!( ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 9u32), Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, @@ -128,8 +152,14 @@ fn set_blocks_per_round_fails_if_below_total_selected() { #[test] fn set_blocks_per_round_fails_if_equal_to_total_selected() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); - assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 9u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 10u32 + )); + assert_ok!(ParachainStaking::set_total_selected( + RuntimeOrigin::root(), + 9u32 + )); assert_noop!( ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 9u32), Error::::RoundLengthMustBeGreaterThanTotalSelectedCollators, @@ -141,7 +171,10 @@ fn set_blocks_per_round_fails_if_equal_to_total_selected() { fn set_blocks_per_round_passes_if_above_total_selected() { ExtBuilder::default().build().execute_with(|| { assert_eq!(ParachainStaking::round().length, 5); // test relies on this - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 6u32 + )); }); } @@ -149,10 +182,16 @@ fn set_blocks_per_round_passes_if_above_total_selected() { fn set_total_selected_storage_updates_correctly() { ExtBuilder::default().build().execute_with(|| { // round length must be >= total_selected, so update that first - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 10u32 + )); assert_eq!(ParachainStaking::total_selected(), 5u32); - assert_ok!(ParachainStaking::set_total_selected(RuntimeOrigin::root(), 6u32)); + assert_ok!(ParachainStaking::set_total_selected( + RuntimeOrigin::root(), + 6u32 + )); assert_eq!(ParachainStaking::total_selected(), 6u32); }); } @@ -196,12 +235,18 @@ fn set_collator_commission_event_emits_correctly() { #[test] fn set_collator_commission_storage_updates_correctly() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(ParachainStaking::collator_commission(), Perbill::from_percent(20)); + assert_eq!( + ParachainStaking::collator_commission(), + Perbill::from_percent(20) + ); assert_ok!(ParachainStaking::set_collator_commission( RuntimeOrigin::root(), Perbill::from_percent(5) )); - assert_eq!(ParachainStaking::collator_commission(), Perbill::from_percent(5)); + assert_eq!( + ParachainStaking::collator_commission(), + Perbill::from_percent(5) + ); }); } @@ -223,15 +268,18 @@ fn cannot_set_collator_commission_to_current_collator_commission() { #[test] fn set_blocks_per_round_event_emits_correctly() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 6u32 + )); assert_events_eq!(Event::BlocksPerRoundSet { current_round: 1, first_block: 0, old: 5, new: 6, - new_per_round_inflation_min: Perbill::from_parts(570), - new_per_round_inflation_ideal: Perbill::from_parts(570), - new_per_round_inflation_max: Perbill::from_parts(570), + new_per_round_inflation_min: Perbill::from_parts(463), + new_per_round_inflation_ideal: Perbill::from_parts(463), + new_per_round_inflation_max: Perbill::from_parts(463), }); }); } @@ -240,7 +288,10 @@ fn set_blocks_per_round_event_emits_correctly() { fn set_blocks_per_round_storage_updates_correctly() { ExtBuilder::default().build().execute_with(|| { assert_eq!(ParachainStaking::round().length, 5); - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 6u32 + )); assert_eq!(ParachainStaking::round().length, 6); }); } @@ -275,7 +326,10 @@ fn round_immediately_jumps_if_current_duration_exceeds_new_blocks_per_round() { // we can't lower the blocks per round because it must be above the number of collators, // and we can't lower the number of collators because it must be above // MinSelectedCandidates. so we first raise blocks per round, then lower it. - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 10u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 10u32 + )); roll_to(10); assert_events_emitted!(Event::NewRound { @@ -285,7 +339,10 @@ fn round_immediately_jumps_if_current_duration_exceeds_new_blocks_per_round() { total_balance: 20 },); roll_to(17); - assert_ok!(ParachainStaking::set_blocks_per_round(RuntimeOrigin::root(), 6u32)); + assert_ok!(ParachainStaking::set_blocks_per_round( + RuntimeOrigin::root(), + 6u32 + )); roll_to(18); assert_events_emitted!(Event::NewRound { starting_block: 18, @@ -304,7 +361,11 @@ fn invalid_monetary_origin_fails() { assert_noop!( ParachainStaking::set_staking_expectations( RuntimeOrigin::signed(45), - Range { min: 3u32.into(), ideal: 4u32.into(), max: 5u32.into() } + Range { + min: 3u32.into(), + ideal: 4u32.into(), + max: 5u32.into() + } ), sp_runtime::DispatchError::BadOrigin ); @@ -352,7 +413,11 @@ fn set_staking_event_emits_event_correctly() { // valid call succeeds assert_ok!(ParachainStaking::set_staking_expectations( RuntimeOrigin::root(), - Range { min: 3u128, ideal: 4u128, max: 5u128 } + Range { + min: 3u128, + ideal: 4u128, + max: 5u128, + } )); assert_events_eq!(Event::StakeExpectationsSet { expect_min: 3u128, @@ -367,15 +432,27 @@ fn set_staking_updates_storage_correctly() { ExtBuilder::default().build().execute_with(|| { assert_eq!( ParachainStaking::inflation_config().expect, - Range { min: 700, ideal: 700, max: 700 } + Range { + min: 700, + ideal: 700, + max: 700 + } ); assert_ok!(ParachainStaking::set_staking_expectations( RuntimeOrigin::root(), - Range { min: 3u128, ideal: 4u128, max: 5u128 } + Range { + min: 3u128, + ideal: 4u128, + max: 5u128, + } )); assert_eq!( ParachainStaking::inflation_config().expect, - Range { min: 3u128, ideal: 4u128, max: 5u128 } + Range { + min: 3u128, + ideal: 4u128, + max: 5u128 + } ); }); } @@ -387,7 +464,11 @@ fn cannot_set_invalid_staking_expectations() { assert_noop!( ParachainStaking::set_staking_expectations( RuntimeOrigin::root(), - Range { min: 5u128, ideal: 4u128, max: 3u128 } + Range { + min: 5u128, + ideal: 4u128, + max: 3u128 + } ), Error::::InvalidSchedule ); @@ -399,12 +480,20 @@ fn cannot_set_same_staking_expectations() { ExtBuilder::default().build().execute_with(|| { assert_ok!(ParachainStaking::set_staking_expectations( RuntimeOrigin::root(), - Range { min: 3u128, ideal: 4u128, max: 5u128 } + Range { + min: 3u128, + ideal: 4u128, + max: 5u128 + } )); assert_noop!( ParachainStaking::set_staking_expectations( RuntimeOrigin::root(), - Range { min: 3u128, ideal: 4u128, max: 5u128 } + Range { + min: 3u128, + ideal: 4u128, + max: 5u128 + } ), Error::::NoWritingSameValue ); @@ -416,8 +505,11 @@ fn cannot_set_same_staking_expectations() { #[test] fn set_inflation_event_emits_correctly() { ExtBuilder::default().build().execute_with(|| { - let (min, ideal, max): (Perbill, Perbill, Perbill) = - (Perbill::from_percent(3), Perbill::from_percent(4), Perbill::from_percent(5)); + let (min, ideal, max): (Perbill, Perbill, Perbill) = ( + Perbill::from_percent(3), + Perbill::from_percent(4), + Perbill::from_percent(5), + ); assert_ok!(ParachainStaking::set_inflation( RuntimeOrigin::root(), Range { min, ideal, max } @@ -426,7 +518,7 @@ fn set_inflation_event_emits_correctly() { annual_min: min, annual_ideal: ideal, annual_max: max, - round_min: Perbill::from_parts(28), + round_min: Perbill::from_parts(29), round_ideal: Perbill::from_parts(38), round_max: Perbill::from_parts(47), }); @@ -436,8 +528,11 @@ fn set_inflation_event_emits_correctly() { #[test] fn set_inflation_storage_updates_correctly() { ExtBuilder::default().build().execute_with(|| { - let (min, ideal, max): (Perbill, Perbill, Perbill) = - (Perbill::from_percent(3), Perbill::from_percent(4), Perbill::from_percent(5)); + let (min, ideal, max): (Perbill, Perbill, Perbill) = ( + Perbill::from_percent(3), + Perbill::from_percent(4), + Perbill::from_percent(5), + ); assert_eq!( ParachainStaking::inflation_config().annual, Range { @@ -458,11 +553,14 @@ fn set_inflation_storage_updates_correctly() { RuntimeOrigin::root(), Range { min, ideal, max } ),); - assert_eq!(ParachainStaking::inflation_config().annual, Range { min, ideal, max }); + assert_eq!( + ParachainStaking::inflation_config().annual, + Range { min, ideal, max } + ); assert_eq!( ParachainStaking::inflation_config().round, Range { - min: Perbill::from_parts(28), + min: Perbill::from_parts(29), ideal: Perbill::from_parts(38), max: Perbill::from_parts(47) } @@ -490,8 +588,11 @@ fn cannot_set_invalid_inflation() { #[test] fn cannot_set_same_inflation() { ExtBuilder::default().build().execute_with(|| { - let (min, ideal, max): (Perbill, Perbill, Perbill) = - (Perbill::from_percent(3), Perbill::from_percent(4), Perbill::from_percent(5)); + let (min, ideal, max): (Perbill, Perbill, Perbill) = ( + Perbill::from_percent(3), + Perbill::from_percent(4), + Perbill::from_percent(5), + ); assert_ok!(ParachainStaking::set_inflation( RuntimeOrigin::root(), Range { min, ideal, max } @@ -508,7 +609,10 @@ fn cannot_set_same_inflation() { #[test] fn set_parachain_bond_account_event_emits_correctly() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); + assert_ok!(ParachainStaking::set_parachain_bond_account( + RuntimeOrigin::root(), + 11 + )); assert_events_eq!(Event::ParachainBondAccountSet { old: 0, new: 11 }); }); } @@ -517,7 +621,10 @@ fn set_parachain_bond_account_event_emits_correctly() { fn set_parachain_bond_account_storage_updates_correctly() { ExtBuilder::default().build().execute_with(|| { assert_eq!(ParachainStaking::parachain_bond_info().account, 0); - assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); + assert_ok!(ParachainStaking::set_parachain_bond_account( + RuntimeOrigin::root(), + 11 + )); assert_eq!(ParachainStaking::parachain_bond_info().account, 11); }); } @@ -541,12 +648,18 @@ fn set_parachain_bond_reserve_percent_event_emits_correctly() { #[test] fn set_parachain_bond_reserve_percent_storage_updates_correctly() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(ParachainStaking::parachain_bond_info().percent, Percent::from_percent(30)); + assert_eq!( + ParachainStaking::parachain_bond_info().percent, + Percent::from_percent(30) + ); assert_ok!(ParachainStaking::set_parachain_bond_reserve_percent( RuntimeOrigin::root(), Percent::from_percent(50) )); - assert_eq!(ParachainStaking::parachain_bond_info().percent, Percent::from_percent(50)); + assert_eq!( + ParachainStaking::parachain_bond_info().percent, + Percent::from_percent(50) + ); }); } @@ -569,53 +682,89 @@ fn cannot_set_same_parachain_bond_reserve_percent() { #[test] fn join_candidates_event_emits_correctly() { - ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); - assert_events_eq!(Event::JoinedCollatorCandidates { - account: 1, - amount_locked: 10u128, - new_total_amt_locked: 10u128, + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 10u128, + 0u32 + )); + assert_events_eq!(Event::JoinedCollatorCandidates { + account: 1, + amount_locked: 10u128, + new_total_amt_locked: 10u128, + }); }); - }); } #[test] fn join_candidates_reserves_balance() { - ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { - assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); - assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); - }); + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 10u128, + 0u32 + )); + assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); + }); } #[test] fn join_candidates_increases_total_staked() { - ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { - assert_eq!(ParachainStaking::total(), 0); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); - assert_eq!(ParachainStaking::total(), 10); - }); + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_eq!(ParachainStaking::total(), 0); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 10u128, + 0u32 + )); + assert_eq!(ParachainStaking::total(), 10); + }); } #[test] fn join_candidates_creates_candidate_state() { - ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { - assert!(ParachainStaking::candidate_info(1).is_none()); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); - let candidate_state = ParachainStaking::candidate_info(1).expect("just joined => exists"); - assert_eq!(candidate_state.bond, 10u128); - }); + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .build() + .execute_with(|| { + assert!(ParachainStaking::candidate_info(1).is_none()); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 10u128, + 0u32 + )); + let candidate_state = + ParachainStaking::candidate_info(1).expect("just joined => exists"); + assert_eq!(candidate_state.bond, 10u128); + }); } #[test] fn join_candidates_adds_to_candidate_pool() { - ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { - assert!(ParachainStaking::candidate_pool().0.is_empty()); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 10u128, 0u32)); - let candidate_pool = ParachainStaking::candidate_pool(); - assert_eq!(candidate_pool.0[0].owner, 1); - assert_eq!(candidate_pool.0[0].amount, 10); - }); + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .build() + .execute_with(|| { + assert!(ParachainStaking::candidate_pool().0.is_empty()); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 10u128, + 0u32 + )); + let candidate_pool = ParachainStaking::candidate_pool(); + assert_eq!(candidate_pool.0[0].owner, 1); + assert_eq!(candidate_pool.0[0].amount, 10); + }); } #[test] @@ -649,38 +798,52 @@ fn cannot_join_candidates_if_delegator() { #[test] fn cannot_join_candidates_without_min_bond() { - ExtBuilder::default().with_balances(vec![(1, 1000)]).build().execute_with(|| { - assert_noop!( - ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 9u128, 100u32), - Error::::CandidateBondBelowMin - ); - }); + ExtBuilder::default() + .with_balances(vec![(1, 1000)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 9u128, 100u32), + Error::::CandidateBondBelowMin + ); + }); } #[test] fn can_force_join_candidates_without_min_bond() { - ExtBuilder::default().with_balances(vec![(1, 10)]).build().execute_with(|| { - assert_ok!(ParachainStaking::force_join_candidates(RuntimeOrigin::root(), 1, 9, 100u32)); - assert_events_eq!(Event::JoinedCollatorCandidates { - account: 1, - amount_locked: 9u128, - new_total_amt_locked: 9u128, + ExtBuilder::default() + .with_balances(vec![(1, 10)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::force_join_candidates( + RuntimeOrigin::root(), + 1, + 9, + 100u32 + )); + assert_events_eq!(Event::JoinedCollatorCandidates { + account: 1, + amount_locked: 9u128, + new_total_amt_locked: 9u128, + }); }); - }); } #[test] fn cannot_join_candidates_with_more_than_available_balance() { - ExtBuilder::default().with_balances(vec![(1, 500)]).build().execute_with(|| { - assert_noop!( - ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 501u128, 100u32), - DispatchError::Module(ModuleError { - index: 2, - error: [8, 0, 0, 0], - message: Some("InsufficientBalance") - }) - ); - }); + ExtBuilder::default() + .with_balances(vec![(1, 500)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 501u128, 100u32), + DispatchError::Module(ModuleError { + index: 2, + error: [8, 0, 0, 0], + message: Some("InsufficientBalance") + }) + ); + }); } #[test] @@ -718,7 +881,11 @@ fn sufficient_join_candidates_weight_hint_succeeds() { .execute_with(|| { let mut count = 5u32; for i in 6..10 { - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(i), 20, count)); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(i), + 20, + count + )); count += 1u32; } }); @@ -760,7 +927,10 @@ fn leave_candidates_event_emits_correctly() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); assert_events_eq!(Event::CandidateScheduledExit { exit_allowed_round: 1, candidate: 1, @@ -777,7 +947,10 @@ fn leave_candidates_removes_candidate_from_candidate_pool() { .build() .execute_with(|| { assert_eq!(ParachainStaking::candidate_pool().0.len(), 1); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); assert!(ParachainStaking::candidate_pool().0.is_empty()); }); } @@ -799,7 +972,10 @@ fn cannot_leave_candidates_if_already_leaving_candidates() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); assert_noop!( ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32), Error::::CandidateAlreadyLeaving @@ -825,24 +1001,36 @@ fn insufficient_leave_candidates_weight_hint_fails() { #[test] fn enable_marking_offline_works() { - ExtBuilder::default().with_balances(vec![(1, 20)]).build().execute_with(|| { - assert_ok!(ParachainStaking::enable_marking_offline(RuntimeOrigin::root(), true)); - assert!(ParachainStaking::marking_offline()); + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_ok!(ParachainStaking::enable_marking_offline( + RuntimeOrigin::root(), + true + )); + assert!(ParachainStaking::marking_offline()); - // Set to false now - assert_ok!(ParachainStaking::enable_marking_offline(RuntimeOrigin::root(), false)); - assert!(!ParachainStaking::marking_offline()); - }); + // Set to false now + assert_ok!(ParachainStaking::enable_marking_offline( + RuntimeOrigin::root(), + false + )); + assert!(!ParachainStaking::marking_offline()); + }); } #[test] fn enable_marking_offline_fails_bad_origin() { - ExtBuilder::default().with_balances(vec![(1, 20)]).build().execute_with(|| { - assert_noop!( - ParachainStaking::enable_marking_offline(RuntimeOrigin::signed(1), true), - sp_runtime::DispatchError::BadOrigin - ); - }); + ExtBuilder::default() + .with_balances(vec![(1, 20)]) + .build() + .execute_with(|| { + assert_noop!( + ParachainStaking::enable_marking_offline(RuntimeOrigin::signed(1), true), + sp_runtime::DispatchError::BadOrigin + ); + }); } #[test] @@ -880,7 +1068,10 @@ fn notify_inactive_collator_works() { assert_eq!(::RewardPaymentDelay::get(), 2); // Call 'notify_inactive_collator' extrinsic - assert_ok!(ParachainStaking::notify_inactive_collator(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::notify_inactive_collator( + RuntimeOrigin::signed(1), + 1 + )); // Check the collator was marked as offline as it hasn't produced blocks assert_events_eq!(Event::CandidateWentOffline { candidate: 1 },); @@ -923,11 +1114,31 @@ fn notify_inactive_collator_fails_candidate_is_not_collator() { roll_to_round_begin(2); assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 80 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 2, + collator_account: 4, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 2, + collator_account: 5, + total_exposed_amount: 80, + }, Event::NewRound { starting_block: 5, round: 2, @@ -937,16 +1148,40 @@ fn notify_inactive_collator_fails_candidate_is_not_collator() { ); roll_blocks(1); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(6), 10, 100)); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(6), + 10, + 100 + )); // Round 6 roll_to_round_begin(6); assert_events_eq!( - Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 6, collator_account: 5, total_exposed_amount: 80 }, + Event::CollatorChosen { + round: 6, + collator_account: 1, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 6, + collator_account: 2, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 6, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 6, + collator_account: 4, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 6, + collator_account: 5, + total_exposed_amount: 80, + }, Event::NewRound { starting_block: 25, round: 6, @@ -1052,9 +1287,16 @@ fn execute_leave_candidates_emits_event() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 0 + )); assert_events_emitted!(Event::CandidateLeft { ex_candidate: 1, unlocked_amount: 10, @@ -1070,9 +1312,16 @@ fn execute_leave_candidates_callable_by_any_signed() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(2), + 1, + 0 + )); }); } @@ -1084,7 +1333,10 @@ fn execute_leave_candidates_requires_correct_weight_hint() { .with_delegations(vec![(2, 1, 10), (3, 1, 10), (4, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); roll_to(10); for i in 0..3 { assert_noop!( @@ -1092,7 +1344,11 @@ fn execute_leave_candidates_requires_correct_weight_hint() { Error::::TooLowCandidateDelegationCountToLeaveCandidates ); } - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 1, 3)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(2), + 1, + 3 + )); }); } @@ -1104,9 +1360,16 @@ fn execute_leave_candidates_unreserves_balance() { .build() .execute_with(|| { assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 0 + )); assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); }); } @@ -1119,9 +1382,16 @@ fn execute_leave_candidates_decreases_total_staked() { .build() .execute_with(|| { assert_eq!(ParachainStaking::total(), 10); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 0 + )); assert_eq!(ParachainStaking::total(), 0); }); } @@ -1133,13 +1403,20 @@ fn execute_leave_candidates_removes_candidate_state() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); // candidate state is not immediately removed let candidate_state = ParachainStaking::candidate_info(1).expect("just left => still exists"); assert_eq!(candidate_state.bond, 10u128); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 0 + )); assert!(ParachainStaking::candidate_info(1).is_none()); }); } @@ -1157,7 +1434,7 @@ fn execute_leave_candidates_removes_pending_delegation_requests() { 1, 5 )); - let state = ParachainStaking::delegation_scheduled_requests(1); + let state = ParachainStaking::delegation_scheduled_requests(&1); assert_eq!( state, vec![ScheduledRequest { @@ -1166,22 +1443,29 @@ fn execute_leave_candidates_removes_pending_delegation_requests() { action: DelegationAction::Decrease(5), }], ); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); // candidate state is not immediately removed let candidate_state = ParachainStaking::candidate_info(1).expect("just left => still exists"); assert_eq!(candidate_state.bond, 10u128); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 1 + )); assert!(ParachainStaking::candidate_info(1).is_none()); assert!( - !ParachainStaking::delegation_scheduled_requests(1) + !ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2), "delegation request not removed" ); assert!( - !>::contains_key(1), + !>::contains_key(&1), "the key was not removed from storage" ); }); @@ -1194,7 +1478,10 @@ fn cannot_execute_leave_candidates_before_delay() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); assert_noop!( ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(3), 1, 0) .map_err(|err| err.error), @@ -1207,7 +1494,11 @@ fn cannot_execute_leave_candidates_before_delay() { Error::::CandidateCannotLeaveYet ); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(3), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(3), + 1, + 0 + )); }); } @@ -1220,11 +1511,17 @@ fn cancel_leave_candidates_emits_event() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); - assert_ok!(ParachainStaking::cancel_leave_candidates(RuntimeOrigin::signed(1), 1)); - assert_events_emitted!(Event::CancelledCandidateExit { candidate: 1 }); - }); -} + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); + assert_ok!(ParachainStaking::cancel_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); + assert_events_emitted!(Event::CancelledCandidateExit { candidate: 1 }); + }); +} #[test] fn cancel_leave_candidates_updates_candidate_state() { @@ -1233,10 +1530,16 @@ fn cancel_leave_candidates_updates_candidate_state() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); - assert_ok!(ParachainStaking::cancel_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); + assert_ok!(ParachainStaking::cancel_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); let candidate = - ParachainStaking::candidate_info(1).expect("just cancelled leave so exists"); + ParachainStaking::candidate_info(&1).expect("just cancelled leave so exists"); assert!(candidate.is_active()); }); } @@ -1248,8 +1551,14 @@ fn cancel_leave_candidates_adds_to_candidate_pool() { .with_candidates(vec![(1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1u32)); - assert_ok!(ParachainStaking::cancel_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1u32 + )); + assert_ok!(ParachainStaking::cancel_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 10); }); @@ -1402,7 +1711,10 @@ fn cannot_go_online_if_leaving() { .with_candidates(vec![(1, 20)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); assert_noop!( ParachainStaking::go_online(RuntimeOrigin::signed(1)).map_err(|err| err.error), Error::::CannotGoOnlineIfLeaving @@ -1419,7 +1731,10 @@ fn candidate_bond_more_emits_correct_event() { .with_candidates(vec![(1, 20)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_ok!(ParachainStaking::candidate_bond_more( + RuntimeOrigin::signed(1), + 30 + )); assert_events_eq!(Event::CandidateBondedMore { candidate: 1, amount: 30, @@ -1436,7 +1751,10 @@ fn candidate_bond_more_reserves_balance() { .build() .execute_with(|| { assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 30); - assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_ok!(ParachainStaking::candidate_bond_more( + RuntimeOrigin::signed(1), + 30 + )); assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 0); }); } @@ -1449,7 +1767,10 @@ fn candidate_bond_more_increases_total() { .build() .execute_with(|| { let mut total = ParachainStaking::total(); - assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_ok!(ParachainStaking::candidate_bond_more( + RuntimeOrigin::signed(1), + 30 + )); total += 30; assert_eq!(ParachainStaking::total(), total); }); @@ -1464,7 +1785,10 @@ fn candidate_bond_more_updates_candidate_state() { .execute_with(|| { let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); assert_eq!(candidate_state.bond, 20); - assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_ok!(ParachainStaking::candidate_bond_more( + RuntimeOrigin::signed(1), + 30 + )); let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); assert_eq!(candidate_state.bond, 50); }); @@ -1479,7 +1803,10 @@ fn candidate_bond_more_updates_candidate_pool() { .execute_with(|| { assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 20); - assert_ok!(ParachainStaking::candidate_bond_more(RuntimeOrigin::signed(1), 30)); + assert_ok!(ParachainStaking::candidate_bond_more( + RuntimeOrigin::signed(1), + 30 + )); assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 50); }); @@ -1513,7 +1840,10 @@ fn cannot_schedule_candidate_bond_less_if_request_exists() { .with_candidates(vec![(1, 30)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 5)); + assert_ok!(ParachainStaking::schedule_candidate_bond_less( + RuntimeOrigin::signed(1), + 5 + )); assert_noop!( ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 5), Error::::PendingCandidateRequestAlreadyExists @@ -1552,7 +1882,10 @@ fn can_schedule_candidate_bond_less_if_leaving_candidates() { .with_candidates(vec![(1, 30)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); assert_ok!(ParachainStaking::schedule_candidate_bond_less( RuntimeOrigin::signed(1), 10 @@ -1567,9 +1900,16 @@ fn cannot_schedule_candidate_bond_less_if_exited_candidates() { .with_candidates(vec![(1, 30)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 0)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 0 + )); assert_noop!( ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 10), Error::::CandidateDNE @@ -1592,7 +1932,10 @@ fn execute_candidate_bond_less_emits_correct_event() { )); roll_to(10); roll_blocks(1); - assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::execute_candidate_bond_less( + RuntimeOrigin::signed(1), + 1 + )); assert_events_eq!(Event::CandidateBondedLess { candidate: 1, amount: 30, @@ -1614,7 +1957,10 @@ fn execute_candidate_bond_less_unreserves_balance() { 10 )); roll_to(10); - assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::execute_candidate_bond_less( + RuntimeOrigin::signed(1), + 1 + )); assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&1), 10); }); } @@ -1632,7 +1978,10 @@ fn execute_candidate_bond_less_decreases_total() { 10 )); roll_to(10); - assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::execute_candidate_bond_less( + RuntimeOrigin::signed(1), + 1 + )); total -= 10; assert_eq!(ParachainStaking::total(), total); }); @@ -1652,7 +2001,10 @@ fn execute_candidate_bond_less_updates_candidate_state() { 10 )); roll_to(10); - assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::execute_candidate_bond_less( + RuntimeOrigin::signed(1), + 1 + )); let candidate_state = ParachainStaking::candidate_info(1).expect("updated => exists"); assert_eq!(candidate_state.bond, 20); }); @@ -1672,7 +2024,10 @@ fn execute_candidate_bond_less_updates_candidate_pool() { 10 )); roll_to(10); - assert_ok!(ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::execute_candidate_bond_less( + RuntimeOrigin::signed(1), + 1 + )); assert_eq!(ParachainStaking::candidate_pool().0[0].owner, 1); assert_eq!(ParachainStaking::candidate_pool().0[0].amount, 20); }); @@ -1691,7 +2046,9 @@ fn cancel_candidate_bond_less_emits_event() { RuntimeOrigin::signed(1), 10 )); - assert_ok!(ParachainStaking::cancel_candidate_bond_less(RuntimeOrigin::signed(1))); + assert_ok!(ParachainStaking::cancel_candidate_bond_less( + RuntimeOrigin::signed(1) + )); assert_events_emitted!(Event::CancelledCandidateBondLess { candidate: 1, amount: 10, @@ -1711,8 +2068,13 @@ fn cancel_candidate_bond_less_updates_candidate_state() { RuntimeOrigin::signed(1), 10 )); - assert_ok!(ParachainStaking::cancel_candidate_bond_less(RuntimeOrigin::signed(1))); - assert!(ParachainStaking::candidate_info(1).unwrap().request.is_none()); + assert_ok!(ParachainStaking::cancel_candidate_bond_less( + RuntimeOrigin::signed(1) + )); + assert!(ParachainStaking::candidate_info(&1) + .unwrap() + .request + .is_none()); }); } @@ -1743,7 +2105,13 @@ fn delegate_event_emits_correctly() { .with_candidates(vec![(1, 30)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 10, + 0, + 0 + )); assert_events_eq!(Event::Delegation { delegator: 2, locked_amount: 10, @@ -1761,9 +2129,15 @@ fn delegate_reserves_balance() { .with_candidates(vec![(1, 30)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 10); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 10, + 0, + 0 + )); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 0); }); } @@ -1775,7 +2149,13 @@ fn delegate_updates_delegator_state() { .build() .execute_with(|| { assert!(ParachainStaking::delegator_state(2).is_none()); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 10, + 0, + 0 + )); let delegator_state = ParachainStaking::delegator_state(2).expect("just delegated => exists"); assert_eq!(delegator_state.total(), 10); @@ -1798,7 +2178,13 @@ fn delegate_updates_collator_state() { ParachainStaking::top_delegations(1).expect("registered in genesis"); assert!(top_delegations.delegations.is_empty()); assert!(top_delegations.total.is_zero()); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 0, 0)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 10, + 0, + 0 + )); let candidate_state = ParachainStaking::candidate_info(1).expect("just delegated => exists"); assert_eq!(candidate_state.total_counted, 40); @@ -1816,8 +2202,18 @@ fn can_delegate_immediately_after_other_join_candidates() { .with_balances(vec![(1, 20), (2, 20)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 20, 0)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 20, 0, 0)); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 20, + 0 + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 20, + 0, + 0 + )); }); } @@ -1829,8 +2225,17 @@ fn can_delegate_if_revoking() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 4, 10, 0, 2)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 4, + 10, + 0, + 2 + )); }); } @@ -1899,13 +2304,22 @@ fn can_delegate_if_full_and_new_delegation_greater_than_lowest_bottom() { ]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(11), 1, 11, 8, 0)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(11), + 1, + 11, + 8, + 0 + )); assert_events_emitted!(Event::DelegationKicked { delegator: 10, candidate: 1, unstaked_amount: 10 }); - assert_events_emitted!(Event::DelegatorLeft { delegator: 10, unstaked_amount: 10 }); + assert_events_emitted!(Event::DelegatorLeft { + delegator: 10, + unstaked_amount: 10 + }); }); } @@ -1917,8 +2331,17 @@ fn can_still_delegate_if_leaving() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 3, 10, 0, 1),); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 3, + 10, + 0, + 1 + ),); }); } @@ -1996,14 +2419,16 @@ fn sufficient_delegate_weight_hint_succeeds() { )); count += 1u32; } - for (count, i) in (3..11).enumerate() { + let mut count = 0u32; + for i in 3..11 { assert_ok!(ParachainStaking::delegate( RuntimeOrigin::signed(i), 2, 10, - count as u32, + count, 1u32 )); + count += 1u32; } }); } @@ -2067,7 +2492,10 @@ fn revoke_delegation_event_emits_correctly() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_events_eq!(Event::DelegationRevocationScheduled { round: 1, delegator: 2, @@ -2088,7 +2516,11 @@ fn revoke_delegation_event_emits_correctly() { unstaked_amount: 10, total_candidate_staked: 30 }, - Event::DelegationRevoked { delegator: 2, candidate: 1, unstaked_amount: 10 }, + Event::DelegationRevoked { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + }, ); }); } @@ -2101,9 +2533,15 @@ fn can_revoke_delegation_if_revoking_another_delegation() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); // this is an exit implicitly because last delegation revoked - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3 + )); }); } @@ -2140,7 +2578,10 @@ fn can_schedule_revoke_delegation_below_min_delegator_stake() { .with_delegations(vec![(2, 1, 5), (2, 3, 3)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); }); } @@ -2154,9 +2595,13 @@ fn delegator_bond_more_reserves_balance() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 5); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 5); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 0); }); } @@ -2169,7 +2614,11 @@ fn delegator_bond_more_increases_total_staked() { .build() .execute_with(|| { assert_eq!(ParachainStaking::total(), 40); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); assert_eq!(ParachainStaking::total(), 45); }); } @@ -2182,9 +2631,23 @@ fn delegator_bond_more_updates_delegator_state() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 10); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); - assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 15); + assert_eq!( + ParachainStaking::delegator_state(2) + .expect("exists") + .total(), + 10 + ); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); + assert_eq!( + ParachainStaking::delegator_state(2) + .expect("exists") + .total(), + 15 + ); }); } @@ -2196,12 +2659,28 @@ fn delegator_bond_more_updates_candidate_state_top_delegations() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 10); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, + 2 + ); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, + 10 + ); assert_eq!(ParachainStaking::top_delegations(1).unwrap().total, 10); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 15); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, + 2 + ); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, + 15 + ); assert_eq!(ParachainStaking::top_delegations(1).unwrap().total, 15); }); } @@ -2211,19 +2690,35 @@ fn delegator_bond_more_updates_candidate_state_bottom_delegations() { ExtBuilder::default() .with_balances(vec![(1, 30), (2, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10), (3, 1, 20), (4, 1, 20), (5, 1, 20), (6, 1, 20)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 20), + (4, 1, 20), + (5, 1, 20), + (6, 1, 20), + ]) .build() .execute_with(|| { assert_eq!( - ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].owner, + ParachainStaking::bottom_delegations(1) + .expect("exists") + .delegations[0] + .owner, 2 ); assert_eq!( - ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].amount, + ParachainStaking::bottom_delegations(1) + .expect("exists") + .delegations[0] + .amount, 10 ); assert_eq!(ParachainStaking::bottom_delegations(1).unwrap().total, 10); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); assert_events_eq!(Event::DelegationIncreased { delegator: 2, candidate: 1, @@ -2231,11 +2726,17 @@ fn delegator_bond_more_updates_candidate_state_bottom_delegations() { in_top: false }); assert_eq!( - ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].owner, + ParachainStaking::bottom_delegations(1) + .expect("exists") + .delegations[0] + .owner, 2 ); assert_eq!( - ParachainStaking::bottom_delegations(1).expect("exists").delegations[0].amount, + ParachainStaking::bottom_delegations(1) + .expect("exists") + .delegations[0] + .amount, 15 ); assert_eq!(ParachainStaking::bottom_delegations(1).unwrap().total, 15); @@ -2251,7 +2752,11 @@ fn delegator_bond_more_increases_total() { .build() .execute_with(|| { assert_eq!(ParachainStaking::total(), 40); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); assert_eq!(ParachainStaking::total(), 45); }); } @@ -2264,8 +2769,15 @@ fn can_delegator_bond_more_for_leaving_candidate() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); }); } @@ -2277,7 +2789,10 @@ fn delegator_bond_more_disallowed_when_revoke_scheduled() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_noop!( ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5), >::PendingDelegationRevoke @@ -2298,7 +2813,11 @@ fn delegator_bond_more_allowed_when_bond_decrease_scheduled() { 1, 5, )); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 1, 5)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 1, + 5 + )); }); } @@ -2339,7 +2858,7 @@ fn delegator_bond_less_updates_delegator_state() { 1, 5 )); - let state = ParachainStaking::delegation_scheduled_requests(1); + let state = ParachainStaking::delegation_scheduled_requests(&1); assert_eq!( state, vec![ScheduledRequest { @@ -2359,7 +2878,10 @@ fn cannot_delegator_bond_less_if_revoking() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_noop!( ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 1) .map_err(|err| err.error), @@ -2456,7 +2978,10 @@ fn execute_revoke_delegation_emits_exit_event_if_exit_happens() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2469,7 +2994,10 @@ fn execute_revoke_delegation_emits_exit_event_if_exit_happens() { unstaked_amount: 10, total_candidate_staked: 30 }); - assert_events_emitted!(Event::DelegatorLeft { delegator: 2, unstaked_amount: 10 }); + assert_events_emitted!(Event::DelegatorLeft { + delegator: 2, + unstaked_amount: 10 + }); }); } @@ -2482,7 +3010,10 @@ fn revoke_delegation_executes_exit_if_last_delegation() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2495,7 +3026,10 @@ fn revoke_delegation_executes_exit_if_last_delegation() { unstaked_amount: 10, total_candidate_staked: 30 }); - assert_events_emitted!(Event::DelegatorLeft { delegator: 2, unstaked_amount: 10 }); + assert_events_emitted!(Event::DelegatorLeft { + delegator: 2, + unstaked_amount: 10 + }); }); } @@ -2507,7 +3041,10 @@ fn execute_revoke_delegation_emits_correct_event() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2531,15 +3068,18 @@ fn execute_revoke_delegation_unreserves_balance() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 0); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), 2, 1 )); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 10); }); } @@ -2551,11 +3091,14 @@ fn execute_revoke_delegation_adds_revocation_to_delegator_state() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert!(!ParachainStaking::delegation_scheduled_requests(1) + assert!(!ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); - assert!(ParachainStaking::delegation_scheduled_requests(1) + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); + assert!(ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2)); }); @@ -2569,14 +3112,17 @@ fn execute_revoke_delegation_removes_revocation_from_delegator_state_upon_execut .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), 2, 1 )); - assert!(!ParachainStaking::delegation_scheduled_requests(1) + assert!(!ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2)); }); @@ -2590,7 +3136,10 @@ fn execute_revoke_delegation_removes_revocation_from_state_for_single_delegation .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2598,7 +3147,7 @@ fn execute_revoke_delegation_removes_revocation_from_state_for_single_delegation 1 )); assert!( - !ParachainStaking::delegation_scheduled_requests(1) + !ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2), "delegation was not removed" @@ -2615,7 +3164,10 @@ fn execute_revoke_delegation_decreases_total_staked() { .build() .execute_with(|| { assert_eq!(ParachainStaking::total(), 40); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2635,7 +3187,10 @@ fn execute_revoke_delegation_for_last_delegation_removes_delegator_state() { .build() .execute_with(|| { assert!(ParachainStaking::delegator_state(2).is_some()); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); // this will be confusing for people // if status is leaving, then execute_delegation_request works if last delegation @@ -2656,8 +3211,16 @@ fn execute_revoke_delegation_removes_delegation_from_candidate_state() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::candidate_info(1).expect("exists").delegation_count, 1u32); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_eq!( + ParachainStaking::candidate_info(1) + .expect("exists") + .delegation_count, + 1u32 + ); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2679,11 +3242,17 @@ fn can_execute_revoke_delegation_for_leaving_candidate() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); - roll_to(10); - // can execute delegation request for leaving candidate - assert_ok!(ParachainStaking::execute_delegation_request( + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); + roll_to(10); + // can execute delegation request for leaving candidate + assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), 2, 1 @@ -2699,14 +3268,24 @@ fn can_execute_leave_candidates_if_revoking_candidate() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); // revocation executes during execute leave candidates (callable by anyone) - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 1 + )); assert!(!ParachainStaking::is_delegator(&2)); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(2), 10); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 10); }); } @@ -2718,8 +3297,15 @@ fn delegator_bond_more_after_revoke_delegation_does_not_effect_exit() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(2), 3, 10)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(2), + 3, + 10 + )); roll_to(100); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -2727,7 +3313,7 @@ fn delegator_bond_more_after_revoke_delegation_does_not_effect_exit() { 1 )); assert!(ParachainStaking::is_delegator(&2)); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 10); }); } @@ -2739,7 +3325,10 @@ fn delegator_bond_less_after_revoke_delegation_does_not_effect_exit() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_events_eq!(Event::DelegationRevocationScheduled { round: 1, delegator: 2, @@ -2775,11 +3364,20 @@ fn delegator_bond_less_after_revoke_delegation_does_not_effect_exit() { unstaked_amount: 10, total_candidate_staked: 30, }, - Event::DelegationRevoked { delegator: 2, candidate: 1, unstaked_amount: 10 }, - Event::DelegationDecreased { delegator: 2, candidate: 3, amount: 2, in_top: true }, + Event::DelegationRevoked { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + }, + Event::DelegationDecreased { + delegator: 2, + candidate: 3, + amount: 2, + in_top: true + }, ); assert!(ParachainStaking::is_delegator(&2)); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 22); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 22); }); } @@ -2793,7 +3391,7 @@ fn execute_delegator_bond_less_unreserves_balance() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 0); assert_ok!(ParachainStaking::schedule_delegator_bond_less( RuntimeOrigin::signed(2), 1, @@ -2805,7 +3403,7 @@ fn execute_delegator_bond_less_unreserves_balance() { 2, 1 )); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 5); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 5); }); } @@ -2841,7 +3439,12 @@ fn execute_delegator_bond_less_updates_delegator_state() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 10); + assert_eq!( + ParachainStaking::delegator_state(2) + .expect("exists") + .total(), + 10 + ); assert_ok!(ParachainStaking::schedule_delegator_bond_less( RuntimeOrigin::signed(2), 1, @@ -2853,7 +3456,12 @@ fn execute_delegator_bond_less_updates_delegator_state() { 2, 1 )); - assert_eq!(ParachainStaking::delegator_state(2).expect("exists").total(), 5); + assert_eq!( + ParachainStaking::delegator_state(2) + .expect("exists") + .total(), + 5 + ); }); } @@ -2865,8 +3473,14 @@ fn execute_delegator_bond_less_updates_candidate_state() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 10); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, + 2 + ); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, + 10 + ); assert_ok!(ParachainStaking::schedule_delegator_bond_less( RuntimeOrigin::signed(2), 1, @@ -2878,8 +3492,14 @@ fn execute_delegator_bond_less_updates_candidate_state() { 2, 1 )); - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, 2); - assert_eq!(ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, 5); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].owner, + 2 + ); + assert_eq!( + ParachainStaking::top_delegations(1).unwrap().delegations[0].amount, + 5 + ); }); } @@ -2912,15 +3532,21 @@ fn execute_delegator_bond_less_updates_just_bottom_delegations() { ExtBuilder::default() .with_balances(vec![(1, 20), (2, 10), (3, 11), (4, 12), (5, 14), (6, 15)]) .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(2, 1, 10), (3, 1, 11), (4, 1, 12), (5, 1, 14), (6, 1, 15)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 11), + (4, 1, 12), + (5, 1, 14), + (6, 1, 15), + ]) .build() .execute_with(|| { let pre_call_candidate_info = - ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + ParachainStaking::candidate_info(&1).expect("delegated by all so exists"); let pre_call_top_delegations = - ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + ParachainStaking::top_delegations(&1).expect("delegated by all so exists"); let pre_call_bottom_delegations = - ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + ParachainStaking::bottom_delegations(&1).expect("delegated by all so exists"); assert_ok!(ParachainStaking::schedule_delegator_bond_less( RuntimeOrigin::signed(2), 1, @@ -2933,31 +3559,39 @@ fn execute_delegator_bond_less_updates_just_bottom_delegations() { 1 )); let post_call_candidate_info = - ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + ParachainStaking::candidate_info(&1).expect("delegated by all so exists"); let post_call_top_delegations = - ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + ParachainStaking::top_delegations(&1).expect("delegated by all so exists"); let post_call_bottom_delegations = - ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + ParachainStaking::bottom_delegations(&1).expect("delegated by all so exists"); let mut not_equal = false; for Bond { owner, amount } in pre_call_bottom_delegations.delegations { - for Bond { owner: post_owner, amount: post_amount } in - &post_call_bottom_delegations.delegations + for Bond { + owner: post_owner, + amount: post_amount, + } in &post_call_bottom_delegations.delegations { - if &owner == post_owner && &amount != post_amount { - not_equal = true; - break; + if &owner == post_owner { + if &amount != post_amount { + not_equal = true; + break; + } } } } assert!(not_equal); let mut equal = true; for Bond { owner, amount } in pre_call_top_delegations.delegations { - for Bond { owner: post_owner, amount: post_amount } in - &post_call_top_delegations.delegations + for Bond { + owner: post_owner, + amount: post_amount, + } in &post_call_top_delegations.delegations { - if &owner == post_owner && &amount != post_amount { - equal = false; - break; + if &owner == post_owner { + if &amount != post_amount { + equal = false; + break; + } } } } @@ -2974,15 +3608,21 @@ fn execute_delegator_bond_less_does_not_delete_bottom_delegations() { ExtBuilder::default() .with_balances(vec![(1, 20), (2, 10), (3, 11), (4, 12), (5, 14), (6, 15)]) .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(2, 1, 10), (3, 1, 11), (4, 1, 12), (5, 1, 14), (6, 1, 15)]) + .with_delegations(vec![ + (2, 1, 10), + (3, 1, 11), + (4, 1, 12), + (5, 1, 14), + (6, 1, 15), + ]) .build() .execute_with(|| { let pre_call_candidate_info = - ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + ParachainStaking::candidate_info(&1).expect("delegated by all so exists"); let pre_call_top_delegations = - ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + ParachainStaking::top_delegations(&1).expect("delegated by all so exists"); let pre_call_bottom_delegations = - ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + ParachainStaking::bottom_delegations(&1).expect("delegated by all so exists"); assert_ok!(ParachainStaking::schedule_delegator_bond_less( RuntimeOrigin::signed(6), 1, @@ -2995,31 +3635,39 @@ fn execute_delegator_bond_less_does_not_delete_bottom_delegations() { 1 )); let post_call_candidate_info = - ParachainStaking::candidate_info(1).expect("delegated by all so exists"); + ParachainStaking::candidate_info(&1).expect("delegated by all so exists"); let post_call_top_delegations = - ParachainStaking::top_delegations(1).expect("delegated by all so exists"); + ParachainStaking::top_delegations(&1).expect("delegated by all so exists"); let post_call_bottom_delegations = - ParachainStaking::bottom_delegations(1).expect("delegated by all so exists"); + ParachainStaking::bottom_delegations(&1).expect("delegated by all so exists"); let mut equal = true; for Bond { owner, amount } in pre_call_bottom_delegations.delegations { - for Bond { owner: post_owner, amount: post_amount } in - &post_call_bottom_delegations.delegations + for Bond { + owner: post_owner, + amount: post_amount, + } in &post_call_bottom_delegations.delegations { - if &owner == post_owner && &amount != post_amount { - equal = false; - break; + if &owner == post_owner { + if &amount != post_amount { + equal = false; + break; + } } } } assert!(equal); let mut not_equal = false; for Bond { owner, amount } in pre_call_top_delegations.delegations { - for Bond { owner: post_owner, amount: post_amount } in - &post_call_top_delegations.delegations + for Bond { + owner: post_owner, + amount: post_amount, + } in &post_call_top_delegations.delegations { - if &owner == post_owner && &amount != post_amount { - not_equal = true; - break; + if &owner == post_owner { + if &amount != post_amount { + not_equal = true; + break; + } } } } @@ -3039,7 +3687,10 @@ fn can_execute_delegator_bond_less_for_leaving_candidate() { .with_delegations(vec![(2, 1, 15)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 1)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 1 + )); assert_ok!(ParachainStaking::schedule_delegator_bond_less( RuntimeOrigin::signed(2), 1, @@ -3066,8 +3717,14 @@ fn cancel_revoke_delegation_emits_correct_event() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1 + )); assert_events_emitted!(Event::CancelledDelegationRequest { delegator: 2, collator: 1, @@ -3087,8 +3744,11 @@ fn cancel_revoke_delegation_updates_delegator_state() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); - let state = ParachainStaking::delegation_scheduled_requests(1); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); + let state = ParachainStaking::delegation_scheduled_requests(&1); assert_eq!( state, vec![ScheduledRequest { @@ -3098,17 +3758,20 @@ fn cancel_revoke_delegation_updates_delegator_state() { }], ); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 10 ); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); - assert!(!ParachainStaking::delegation_scheduled_requests(1) + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1 + )); + assert!(!ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2)); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 0 @@ -3131,7 +3794,10 @@ fn cancel_delegator_bond_less_correct_event() { 1, 5 )); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1 + )); assert_events_emitted!(Event::CancelledDelegationRequest { delegator: 2, collator: 1, @@ -3156,7 +3822,7 @@ fn cancel_delegator_bond_less_updates_delegator_state() { 1, 5 )); - let state = ParachainStaking::delegation_scheduled_requests(1); + let state = ParachainStaking::delegation_scheduled_requests(&1); assert_eq!( state, vec![ScheduledRequest { @@ -3166,17 +3832,20 @@ fn cancel_delegator_bond_less_updates_delegator_state() { }], ); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 5 ); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); - assert!(!ParachainStaking::delegation_scheduled_requests(1) + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1 + )); + assert!(!ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2)); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 0 @@ -3194,9 +3863,12 @@ fn delegator_schedule_revocation_total() { .with_delegations(vec![(2, 1, 10), (2, 3, 10), (2, 4, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 10 @@ -3208,16 +3880,28 @@ fn delegator_schedule_revocation_total() { 1 )); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 0 ); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 5, 10, 0, 2)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 4)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 5, + 10, + 0, + 2 + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3 + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 4 + )); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 20, @@ -3229,7 +3913,7 @@ fn delegator_schedule_revocation_total() { 3 )); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 10, @@ -3240,7 +3924,7 @@ fn delegator_schedule_revocation_total() { 4 )); assert_eq!( - ParachainStaking::delegator_state(2) + ParachainStaking::delegator_state(&2) .map(|x| x.less_total) .expect("delegator state must exist"), 0 @@ -3266,22 +3950,51 @@ fn parachain_bond_inflation_reserve_matches_config() { (11, 1), ]) .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) - .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .with_delegations(vec![ + (6, 1, 10), + (7, 1, 10), + (8, 2, 10), + (9, 2, 10), + (10, 1, 10), + ]) .build() .execute_with(|| { - assert_eq!(Balances::free_balance(11), 1); + assert_eq!(Balances::free_balance(&11), 1); // set parachain bond account so DefaultParachainBondReservePercent = 30% of inflation // is allocated to this account hereafter - assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); + assert_ok!(ParachainStaking::set_parachain_bond_account( + RuntimeOrigin::root(), + 11 + )); assert_events_eq!(Event::ParachainBondAccountSet { old: 0, new: 11 }); roll_to_round_begin(2); // chooses top TotalSelectedCandidates (5), in order assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 10 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 5, round: 2, @@ -3289,12 +4002,12 @@ fn parachain_bond_inflation_reserve_matches_config() { total_balance: 140, }, ); - assert_eq!(Balances::free_balance(11), 1); + assert_eq!(Balances::free_balance(&11), 1); // ~ set block author as 1 for all blocks this round set_author(2, 1, 100); roll_to_round_begin(4); // distribute total issuance to collator 1 and its delegators 6, 7, 19 - assert_eq!(Balances::free_balance(11), 16); + assert_eq!(Balances::free_balance(&11), 16); // ~ set block author as 1 for all blocks this round set_author(3, 1, 100); set_author(4, 1, 100); @@ -3304,38 +4017,103 @@ fn parachain_bond_inflation_reserve_matches_config() { ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(66), 1), Error::::DelegatorDNE ); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(6), 1,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(6), + 1, + )); assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 15 }, - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 4, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 15, + }, + Event::CollatorChosen { + round: 4, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 4, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 4, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 4, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 4, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 15, round: 4, selected_collators_number: 5, total_balance: 140, }, - Event::DelegatorExitScheduled { round: 4, delegator: 6, scheduled_exit: 6 }, + Event::DelegatorExitScheduled { + round: 4, + delegator: 6, + scheduled_exit: 6, + }, ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 20 }, - Event::Rewarded { account: 6, rewards: 5 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, + Event::Rewarded { + account: 1, + rewards: 20, + }, + Event::Rewarded { + account: 6, + rewards: 5, + }, + Event::Rewarded { + account: 7, + rewards: 5, + }, + Event::Rewarded { + account: 10, + rewards: 5, + }, ); // fast forward to block in which delegator 6 exit executes roll_to_round_begin(5); assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 16 }, - Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 5, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 5, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 5, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 16, + }, + Event::CollatorChosen { + round: 5, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 5, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 5, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 5, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 5, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 20, round: 5, @@ -3345,10 +4123,22 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 21 }, - Event::Rewarded { account: 6, rewards: 5 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, + Event::Rewarded { + account: 1, + rewards: 21, + }, + Event::Rewarded { + account: 6, + rewards: 5, + }, + Event::Rewarded { + account: 7, + rewards: 5, + }, + Event::Rewarded { + account: 10, + rewards: 5, + }, ); roll_to_round_begin(6); assert_ok!(ParachainStaking::execute_delegation_request( @@ -3357,12 +4147,35 @@ fn parachain_bond_inflation_reserve_matches_config() { 10 )); assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 16 }, - Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 6, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 16, + }, + Event::CollatorChosen { + round: 6, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 6, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 6, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 6, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 6, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 25, round: 6, @@ -3375,23 +4188,61 @@ fn parachain_bond_inflation_reserve_matches_config() { unstaked_amount: 10, total_candidate_staked: 40, }, - Event::DelegatorLeft { delegator: 6, unstaked_amount: 10 }, + Event::DelegatorLeft { + delegator: 6, + unstaked_amount: 10, + }, ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 22 }, - Event::Rewarded { account: 6, rewards: 6 }, - Event::Rewarded { account: 7, rewards: 6 }, - Event::Rewarded { account: 10, rewards: 6 }, + Event::Rewarded { + account: 1, + rewards: 22, + }, + Event::Rewarded { + account: 6, + rewards: 6, + }, + Event::Rewarded { + account: 7, + rewards: 6, + }, + Event::Rewarded { + account: 10, + rewards: 6, + }, ); roll_to_round_begin(7); assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 17 }, - Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 7, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 7, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 17, + }, + Event::CollatorChosen { + round: 7, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 7, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 7, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 7, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 7, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 30, round: 7, @@ -3401,11 +4252,20 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 26 }, - Event::Rewarded { account: 7, rewards: 7 }, - Event::Rewarded { account: 10, rewards: 7 }, + Event::Rewarded { + account: 1, + rewards: 26, + }, + Event::Rewarded { + account: 7, + rewards: 7, + }, + Event::Rewarded { + account: 10, + rewards: 7, + }, ); - assert_eq!(Balances::free_balance(11), 65); + assert_eq!(Balances::free_balance(&11), 65); roll_blocks(1); assert_ok!(ParachainStaking::set_parachain_bond_reserve_percent( RuntimeOrigin::root(), @@ -3420,12 +4280,35 @@ fn parachain_bond_inflation_reserve_matches_config() { roll_to_round_begin(8); // keep paying 6 assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 30 }, - Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 8, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 30, + }, + Event::CollatorChosen { + round: 8, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 8, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 8, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 8, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 8, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 35, round: 8, @@ -3435,21 +4318,53 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 21 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, + Event::Rewarded { + account: 1, + rewards: 21, + }, + Event::Rewarded { + account: 7, + rewards: 5, + }, + Event::Rewarded { + account: 10, + rewards: 5, + }, ); - assert_eq!(Balances::free_balance(11), 95); + assert_eq!(Balances::free_balance(&11), 95); set_author(7, 1, 100); roll_to_round_begin(9); // no more paying 6 assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 32 }, - Event::CollatorChosen { round: 9, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 9, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 9, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 9, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 9, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 32, + }, + Event::CollatorChosen { + round: 9, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 9, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 9, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 9, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 9, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 40, round: 9, @@ -3459,14 +4374,29 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 22 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, + Event::Rewarded { + account: 1, + rewards: 22, + }, + Event::Rewarded { + account: 7, + rewards: 5, + }, + Event::Rewarded { + account: 10, + rewards: 5, + }, ); - assert_eq!(Balances::free_balance(11), 127); + assert_eq!(Balances::free_balance(&11), 127); set_author(8, 1, 100); roll_blocks(1); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(8), + 1, + 10, + 10, + 10 + )); assert_events_eq!(Event::Delegation { delegator: 8, locked_amount: 10, @@ -3477,12 +4407,35 @@ fn parachain_bond_inflation_reserve_matches_config() { roll_to_round_begin(10); // new delegation is not rewarded yet assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 33 }, - Event::CollatorChosen { round: 10, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 10, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 10, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 10, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 10, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 33, + }, + Event::CollatorChosen { + round: 10, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 10, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 10, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 10, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 10, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 45, round: 10, @@ -3492,22 +4445,54 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 23 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, + Event::Rewarded { + account: 1, + rewards: 23, + }, + Event::Rewarded { + account: 7, + rewards: 5, + }, + Event::Rewarded { + account: 10, + rewards: 5, + }, ); - assert_eq!(Balances::free_balance(11), 160); + assert_eq!(Balances::free_balance(&11), 160); set_author(9, 1, 100); set_author(10, 1, 100); roll_to_round_begin(11); // new delegation is still not rewarded yet assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 35 }, - Event::CollatorChosen { round: 11, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 11, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 11, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 11, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 11, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 35, + }, + Event::CollatorChosen { + round: 11, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 11, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 11, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 11, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 11, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 50, round: 11, @@ -3517,20 +4502,52 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 24 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, + Event::Rewarded { + account: 1, + rewards: 24, + }, + Event::Rewarded { + account: 7, + rewards: 5, + }, + Event::Rewarded { + account: 10, + rewards: 5, + }, ); - assert_eq!(Balances::free_balance(11), 195); + assert_eq!(Balances::free_balance(&11), 195); roll_to_round_begin(12); // new delegation is rewarded, 2 rounds after joining (`RewardPaymentDelay` is 2) assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 37 }, - Event::CollatorChosen { round: 12, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 12, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 12, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 12, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 12, collator_account: 5, total_exposed_amount: 10 }, + Event::ReservedForParachainBond { + account: 11, + value: 37, + }, + Event::CollatorChosen { + round: 12, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 12, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 12, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 12, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 12, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 55, round: 12, @@ -3540,28 +4557,54 @@ fn parachain_bond_inflation_reserve_matches_config() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 24 }, - Event::Rewarded { account: 7, rewards: 4 }, - Event::Rewarded { account: 10, rewards: 4 }, - Event::Rewarded { account: 8, rewards: 4 }, + Event::Rewarded { + account: 1, + rewards: 24, + }, + Event::Rewarded { + account: 7, + rewards: 4, + }, + Event::Rewarded { + account: 10, + rewards: 4, + }, + Event::Rewarded { + account: 8, + rewards: 4, + }, ); - assert_eq!(Balances::free_balance(11), 232); + assert_eq!(Balances::free_balance(&11), 232); }); } #[test] fn paid_collator_commission_matches_config() { ExtBuilder::default() - .with_balances(vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100)]) + .with_balances(vec![ + (1, 100), + (2, 100), + (3, 100), + (4, 100), + (5, 100), + (6, 100), + ]) .with_candidates(vec![(1, 20)]) .with_delegations(vec![(2, 1, 10), (3, 1, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { roll_to_round_begin(2); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(4), 20u128, 100u32)); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(4), + 20u128, + 100u32 + )); assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 40 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 40, + }, Event::NewRound { starting_block: 5, round: 2, @@ -3576,8 +4619,20 @@ fn paid_collator_commission_matches_config() { ); roll_blocks(1); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(5), 4, 10, 10, 10)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 4, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(5), + 4, + 10, + 10, + 10 + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(6), + 4, + 10, + 10, + 10 + )); assert_events_eq!( Event::Delegation { delegator: 5, @@ -3597,8 +4652,16 @@ fn paid_collator_commission_matches_config() { roll_to_round_begin(3); assert_events_eq!( - Event::CollatorChosen { round: 3, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 3, collator_account: 4, total_exposed_amount: 40 }, + Event::CollatorChosen { + round: 3, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 3, + collator_account: 4, + total_exposed_amount: 40, + }, Event::NewRound { starting_block: 10, round: 3, @@ -3612,8 +4675,16 @@ fn paid_collator_commission_matches_config() { // 20% of 10 is commission + due_portion (0) = 2 + 4 = 6 // all delegator payouts are 10-2 = 8 * stake_pct assert_events_eq!( - Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 40 }, + Event::CollatorChosen { + round: 5, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 5, + collator_account: 4, + total_exposed_amount: 40, + }, Event::NewRound { starting_block: 20, round: 5, @@ -3624,9 +4695,18 @@ fn paid_collator_commission_matches_config() { roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 4, rewards: 21 }, - Event::Rewarded { account: 5, rewards: 7 }, - Event::Rewarded { account: 6, rewards: 7 }, + Event::Rewarded { + account: 4, + rewards: 18, + }, + Event::Rewarded { + account: 5, + rewards: 6, + }, + Event::Rewarded { + account: 6, + rewards: 6, + }, ); }); } @@ -3650,16 +4730,23 @@ fn collator_exit_executes_after_delay() { .build() .execute_with(|| { roll_to(11); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(2), 2)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(2), + 2 + )); assert_events_eq!(Event::CandidateScheduledExit { exit_allowed_round: 3, candidate: 2, scheduled_exit: 5, }); - let info = ParachainStaking::candidate_info(2).unwrap(); + let info = ParachainStaking::candidate_info(&2).unwrap(); assert_eq!(info.status, CollatorStatus::Leaving(5)); roll_to(21); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 2, 2)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(2), + 2, + 2 + )); // we must exclude leaving collators from rewards while // holding them retroactively accountable for previous faults // (within the last T::SlashingWindow blocks) @@ -3689,14 +4776,37 @@ fn collator_selection_chooses_top_candidates() { .build() .execute_with(|| { roll_to_round_begin(2); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(6), 6)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(6), + 6 + )); // should choose top TotalSelectedCandidates (5), in order assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 100 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 90 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 70 }, - Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 60 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 100, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 90, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 2, + collator_account: 4, + total_exposed_amount: 70, + }, + Event::CollatorChosen { + round: 2, + collator_account: 5, + total_exposed_amount: 60, + }, Event::NewRound { starting_block: 5, round: 2, @@ -3711,8 +4821,16 @@ fn collator_selection_chooses_top_candidates() { ); roll_to_round_begin(4); roll_blocks(1); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(6), 6, 0)); - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(6), 69u128, 100u32)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(6), + 6, + 0 + )); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(6), + 69u128, + 100u32 + )); assert_events_eq!( Event::CandidateLeft { ex_candidate: 6, @@ -3728,11 +4846,31 @@ fn collator_selection_chooses_top_candidates() { roll_to_round_begin(6); // should choose top TotalSelectedCandidates (5), in order assert_events_eq!( - Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 100 }, - Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 90 }, - Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 70 }, - Event::CollatorChosen { round: 6, collator_account: 6, total_exposed_amount: 69 }, + Event::CollatorChosen { + round: 6, + collator_account: 1, + total_exposed_amount: 100, + }, + Event::CollatorChosen { + round: 6, + collator_account: 2, + total_exposed_amount: 90, + }, + Event::CollatorChosen { + round: 6, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 6, + collator_account: 4, + total_exposed_amount: 70, + }, + Event::CollatorChosen { + round: 6, + collator_account: 6, + total_exposed_amount: 69, + }, Event::NewRound { starting_block: 25, round: 6, @@ -3746,18 +4884,41 @@ fn collator_selection_chooses_top_candidates() { #[test] fn payout_distribution_to_solo_collators() { ExtBuilder::default() - .with_balances(vec![(1, 1000), (2, 1000), (3, 1000), (4, 1000), (7, 33), (8, 33), (9, 33)]) + .with_balances(vec![ + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (7, 33), + (8, 33), + (9, 33), + ]) .with_candidates(vec![(1, 100), (2, 90), (3, 80), (4, 70)]) - .with_rewards_account(999, 100000) .build() .execute_with(|| { roll_to_round_begin(2); // should choose top TotalCandidatesSelected (5), in order assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 100 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 90 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 70 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 100, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 90, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 2, + collator_account: 4, + total_exposed_amount: 70, + }, Event::NewRound { starting_block: 5, round: 2, @@ -3769,10 +4930,26 @@ fn payout_distribution_to_solo_collators() { set_author(2, 1, 100); roll_to_round_begin(4); assert_events_eq!( - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 100 }, - Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 90 }, - Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 70 }, + Event::CollatorChosen { + round: 4, + collator_account: 1, + total_exposed_amount: 100, + }, + Event::CollatorChosen { + round: 4, + collator_account: 2, + total_exposed_amount: 90, + }, + Event::CollatorChosen { + round: 4, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 4, + collator_account: 4, + total_exposed_amount: 70, + }, Event::NewRound { starting_block: 15, round: 4, @@ -3782,7 +4959,10 @@ fn payout_distribution_to_solo_collators() { ); // pay total issuance to 1 at 2nd block roll_blocks(3); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 5205 }); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 205, + }); // ~ set block author as 1 for 3 blocks this round set_author(4, 1, 60); // ~ set block author as 2 for 2 blocks this round @@ -3790,10 +4970,26 @@ fn payout_distribution_to_solo_collators() { roll_to_round_begin(6); // pay 60% total issuance to 1 and 40% total issuance to 2 assert_events_eq!( - Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 100 }, - Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 90 }, - Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 70 }, + Event::CollatorChosen { + round: 6, + collator_account: 1, + total_exposed_amount: 100, + }, + Event::CollatorChosen { + round: 6, + collator_account: 2, + total_exposed_amount: 90, + }, + Event::CollatorChosen { + round: 6, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 6, + collator_account: 4, + total_exposed_amount: 70, + }, Event::NewRound { starting_block: 25, round: 6, @@ -3802,9 +4998,15 @@ fn payout_distribution_to_solo_collators() { }, ); roll_blocks(3); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 3123 }); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 129, + }); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 2, rewards: 2082 },); + assert_events_eq!(Event::Rewarded { + account: 2, + rewards: 86, + },); // ~ each collator produces 1 block this round set_author(6, 1, 20); set_author(6, 2, 20); @@ -3813,10 +5015,26 @@ fn payout_distribution_to_solo_collators() { roll_to_round_begin(8); // pay 20% issuance for all collators assert_events_eq!( - Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 100 }, - Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 90 }, - Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 80 }, - Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 70 }, + Event::CollatorChosen { + round: 8, + collator_account: 1, + total_exposed_amount: 100, + }, + Event::CollatorChosen { + round: 8, + collator_account: 2, + total_exposed_amount: 90, + }, + Event::CollatorChosen { + round: 8, + collator_account: 3, + total_exposed_amount: 80, + }, + Event::CollatorChosen { + round: 8, + collator_account: 4, + total_exposed_amount: 70, + }, Event::NewRound { starting_block: 35, round: 8, @@ -3825,13 +5043,25 @@ fn payout_distribution_to_solo_collators() { }, ); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 3, rewards: 1301 }); + assert_events_eq!(Event::Rewarded { + account: 3, + rewards: 56, + }); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 4, rewards: 1301 }); + assert_events_eq!(Event::Rewarded { + account: 4, + rewards: 56, + }); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 1301 }); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 56, + }); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 2, rewards: 1301 }); + assert_events_eq!(Event::Rewarded { + account: 2, + rewards: 56, + }); // check that distributing rewards clears awarded pts assert!(ParachainStaking::awarded_pts(1, 1).is_zero()); assert!(ParachainStaking::awarded_pts(4, 1).is_zero()); @@ -3859,17 +5089,43 @@ fn multiple_delegations() { (10, 100), ]) .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) - .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) + .with_delegations(vec![ + (6, 1, 10), + (7, 1, 10), + (8, 2, 10), + (9, 2, 10), + (10, 1, 10), + ]) .build() .execute_with(|| { roll_to_round_begin(2); // chooses top TotalSelectedCandidates (5), in order assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 10 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 4, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 5, round: 2, @@ -3878,9 +5134,27 @@ fn multiple_delegations() { }, ); roll_blocks(1); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 2, 10, 10, 10)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 3, 10, 10, 10)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(6), 4, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(6), + 2, + 10, + 10, + 10 + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(6), + 3, + 10, + 10, + 10 + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(6), + 4, + 10, + 10, + 10 + )); assert_events_eq!( Event::Delegation { delegator: 6, @@ -3906,9 +5180,24 @@ fn multiple_delegations() { ); roll_to_round_begin(6); roll_blocks(1); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(7), 2, 80, 10, 10)); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(10), 2, 10, 10, 10)); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(2), 5)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(7), + 2, + 80, + 10, + 10 + )); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(10), + 2, + 10, + 10, + 10 + )); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(2), + 5 + )); assert_events_eq!( Event::Delegation { delegator: 7, @@ -3932,10 +5221,26 @@ fn multiple_delegations() { ); roll_to_round_begin(7); assert_events_eq!( - Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 30 }, - Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 30 }, - Event::CollatorChosen { round: 7, collator_account: 5, total_exposed_amount: 10 }, + Event::CollatorChosen { + round: 7, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 7, + collator_account: 3, + total_exposed_amount: 30, + }, + Event::CollatorChosen { + round: 7, + collator_account: 4, + total_exposed_amount: 30, + }, + Event::CollatorChosen { + round: 7, + collator_account: 5, + total_exposed_amount: 10, + }, Event::NewRound { starting_block: 30, round: 7, @@ -3945,16 +5250,34 @@ fn multiple_delegations() { ); // verify that delegations are removed after collator leaves, not before assert_eq!(ParachainStaking::delegator_state(7).unwrap().total(), 90); - assert_eq!(ParachainStaking::delegator_state(7).unwrap().delegations.0.len(), 2usize); + assert_eq!( + ParachainStaking::delegator_state(7) + .unwrap() + .delegations + .0 + .len(), + 2usize + ); assert_eq!(ParachainStaking::delegator_state(6).unwrap().total(), 40); - assert_eq!(ParachainStaking::delegator_state(6).unwrap().delegations.0.len(), 4usize); - assert_eq!(Balances::locks(6)[0].amount, 40); - assert_eq!(Balances::locks(7)[0].amount, 90); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&6), 60); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&7), 10); + assert_eq!( + ParachainStaking::delegator_state(6) + .unwrap() + .delegations + .0 + .len(), + 4usize + ); + assert_eq!(Balances::locks(&6)[0].amount, 40); + assert_eq!(Balances::locks(&7)[0].amount, 90); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&6), 60); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&7), 10); roll_to_round_begin(8); roll_blocks(1); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 2, 5)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(2), + 2, + 5 + )); assert_events_eq!(Event::CandidateLeft { ex_candidate: 2, unlocked_amount: 140, @@ -3962,10 +5285,24 @@ fn multiple_delegations() { }); assert_eq!(ParachainStaking::delegator_state(7).unwrap().total(), 10); assert_eq!(ParachainStaking::delegator_state(6).unwrap().total(), 30); - assert_eq!(ParachainStaking::delegator_state(7).unwrap().delegations.0.len(), 1usize); - assert_eq!(ParachainStaking::delegator_state(6).unwrap().delegations.0.len(), 3usize); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&6), 70); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&7), 90); + assert_eq!( + ParachainStaking::delegator_state(7) + .unwrap() + .delegations + .0 + .len(), + 1usize + ); + assert_eq!( + ParachainStaking::delegator_state(6) + .unwrap() + .delegations + .0 + .len(), + 3usize + ); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&6), 70); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&7), 90); }); } @@ -3980,21 +5317,31 @@ fn execute_leave_candidate_removes_delegations() { .build() .execute_with(|| { // Verifies the revocation request is initially empty - assert!(!ParachainStaking::delegation_scheduled_requests(2) + assert!(!ParachainStaking::delegation_scheduled_requests(&2) .iter() .any(|x| x.delegator == 3)); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(2), 2)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(3), 2)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(2), + 2 + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(3), + 2 + )); // Verifies the revocation request is present - assert!(ParachainStaking::delegation_scheduled_requests(2) + assert!(ParachainStaking::delegation_scheduled_requests(&2) .iter() .any(|x| x.delegator == 3)); roll_to(16); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(2), 2, 2)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(2), + 2, + 2 + )); // Verifies the revocation request is again empty - assert!(!ParachainStaking::delegation_scheduled_requests(2) + assert!(!ParachainStaking::delegation_scheduled_requests(&2) .iter() .any(|x| x.delegator == 3)); }); @@ -4015,17 +5362,41 @@ fn payouts_follow_delegation_changes() { (10, 100), ]) .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20)]) - .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) - .with_rewards_account(999, 100000) + .with_delegations(vec![ + (6, 1, 10), + (7, 1, 10), + (8, 2, 10), + (9, 2, 10), + (10, 1, 10), + ]) .build() .execute_with(|| { + // ~ set block author as 1 for all blocks this round + set_author(1, 1, 100); + set_author(2, 1, 100); roll_to_round_begin(2); // chooses top TotalSelectedCandidates (5), in order assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 5, round: 2, @@ -4033,15 +5404,33 @@ fn payouts_follow_delegation_changes() { total_balance: 130, }, ); - // ~ set block author as 1 for all blocks this round - set_author(2, 1, 100); + + set_author(3, 1, 100); + set_author(4, 1, 100); + roll_to_round_begin(4); // distribute total issuance to collator 1 and its delegators 6, 7, 19 assert_events_eq!( - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 4, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 4, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 4, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 4, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 15, round: 4, @@ -4051,16 +5440,25 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 2623 }, - Event::Rewarded { account: 6, rewards: 807 }, - Event::Rewarded { account: 7, rewards: 807 }, - Event::Rewarded { account: 10, rewards: 807 }, + Event::Rewarded { + account: 1, + rewards: 23, + }, + Event::Rewarded { + account: 6, + rewards: 7, + }, + Event::Rewarded { + account: 7, + rewards: 7, + }, + Event::Rewarded { + account: 10, + rewards: 7, + }, ); // ~ set block author as 1 for all blocks this round - set_author(3, 1, 100); - set_author(4, 1, 100); set_author(5, 1, 100); - set_author(6, 1, 100); roll_blocks(1); // 1. ensure delegators are paid for 2 rounds after they leave @@ -4068,7 +5466,10 @@ fn payouts_follow_delegation_changes() { ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(66), 1), Error::::DelegatorDNE ); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(6), 1,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(6), + 1, + )); assert_events_eq!(Event::DelegationRevocationScheduled { round: 4, delegator: 6, @@ -4078,10 +5479,26 @@ fn payouts_follow_delegation_changes() { // fast forward to block in which delegator 6 exit executes roll_to_round_begin(5); assert_events_eq!( - Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 5, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 5, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 5, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 5, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 5, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 5, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 20, round: 5, @@ -4091,11 +5508,25 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 2623 }, - Event::Rewarded { account: 6, rewards: 807 }, - Event::Rewarded { account: 7, rewards: 807 }, - Event::Rewarded { account: 10, rewards: 807 }, + Event::Rewarded { + account: 1, + rewards: 24, + }, + Event::Rewarded { + account: 6, + rewards: 8, + }, + Event::Rewarded { + account: 7, + rewards: 8, + }, + Event::Rewarded { + account: 10, + rewards: 8, + }, ); + + set_author(6, 1, 100); // keep paying 6 (note: inflation is in terms of total issuance so that's why 1 is 21) roll_to_round_begin(6); assert_ok!(ParachainStaking::execute_delegation_request( @@ -4104,10 +5535,26 @@ fn payouts_follow_delegation_changes() { 1, )); assert_events_eq!( - Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 6, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 6, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 6, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 6, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 25, round: 6, @@ -4120,25 +5567,60 @@ fn payouts_follow_delegation_changes() { unstaked_amount: 10, total_candidate_staked: 40, }, - Event::DelegationRevoked { delegator: 6, candidate: 1, unstaked_amount: 10 }, - Event::DelegatorLeft { delegator: 6, unstaked_amount: 10 }, + Event::DelegationRevoked { + delegator: 6, + candidate: 1, + unstaked_amount: 10, + }, + Event::DelegatorLeft { + delegator: 6, + unstaked_amount: 10, + }, ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 2623 }, - Event::Rewarded { account: 6, rewards: 807 }, - Event::Rewarded { account: 7, rewards: 807 }, - Event::Rewarded { account: 10, rewards: 807 }, + Event::Rewarded { + account: 1, + rewards: 26, + }, + Event::Rewarded { + account: 6, + rewards: 8, + }, + Event::Rewarded { + account: 7, + rewards: 8, + }, + Event::Rewarded { + account: 10, + rewards: 8, + }, ); // 6 won't be paid for this round because they left already set_author(7, 1, 100); roll_to_round_begin(7); // keep paying 6 assert_events_eq!( - Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 7, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 7, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 7, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 7, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 7, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 30, round: 7, @@ -4148,16 +5630,42 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 3027 }, - Event::Rewarded { account: 7, rewards: 1009 }, - Event::Rewarded { account: 10, rewards: 1009 }, + Event::Rewarded { + account: 1, + rewards: 31, + }, + Event::Rewarded { + account: 7, + rewards: 10, + }, + Event::Rewarded { + account: 10, + rewards: 10, + }, ); + set_author(8, 1, 100); roll_to_round_begin(8); assert_events_eq!( - Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 8, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 8, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 8, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 8, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 35, round: 8, @@ -4167,18 +5675,43 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 3027 }, - Event::Rewarded { account: 7, rewards: 1009 }, - Event::Rewarded { account: 10, rewards: 1009 }, + Event::Rewarded { + account: 1, + rewards: 32, + }, + Event::Rewarded { + account: 7, + rewards: 11, + }, + Event::Rewarded { + account: 10, + rewards: 11, + }, ); - set_author(8, 1, 100); + set_author(9, 1, 100); roll_to_round_begin(9); // no more paying 6 assert_events_eq!( - Event::CollatorChosen { round: 9, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 9, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 9, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 9, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 9, + collator_account: 1, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 9, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 9, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 9, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 40, round: 9, @@ -4188,13 +5721,27 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 3027 }, - Event::Rewarded { account: 7, rewards: 1009 }, - Event::Rewarded { account: 10, rewards: 1009 }, + Event::Rewarded { + account: 1, + rewards: 34, + }, + Event::Rewarded { + account: 7, + rewards: 11, + }, + Event::Rewarded { + account: 10, + rewards: 11, + }, ); roll_blocks(1); - set_author(9, 1, 100); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(8), + 1, + 10, + 10, + 10 + )); assert_events_eq!(Event::Delegation { delegator: 8, locked_amount: 10, @@ -4203,13 +5750,30 @@ fn payouts_follow_delegation_changes() { auto_compound: Percent::zero(), }); + set_author(10, 1, 100); roll_to_round_begin(10); // new delegation is not rewarded yet assert_events_eq!( - Event::CollatorChosen { round: 10, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 10, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 10, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 10, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 10, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 10, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 10, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 10, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 45, round: 10, @@ -4219,18 +5783,42 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 3027 }, - Event::Rewarded { account: 7, rewards: 1009 }, - Event::Rewarded { account: 10, rewards: 1009 }, + Event::Rewarded { + account: 1, + rewards: 36, + }, + Event::Rewarded { + account: 7, + rewards: 12, + }, + Event::Rewarded { + account: 10, + rewards: 12, + }, ); - set_author(10, 1, 100); roll_to_round_begin(11); // new delegation not rewarded yet assert_events_eq!( - Event::CollatorChosen { round: 11, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 11, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 11, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 11, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 11, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 11, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 11, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 11, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 50, round: 11, @@ -4240,18 +5828,43 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 3027 }, - Event::Rewarded { account: 7, rewards: 1009 }, - Event::Rewarded { account: 10, rewards: 1009 }, + Event::Rewarded { + account: 1, + rewards: 37, + }, + Event::Rewarded { + account: 7, + rewards: 12, + }, + Event::Rewarded { + account: 10, + rewards: 12, + }, ); roll_to_round_begin(12); // new delegation is rewarded for first time // 2 rounds after joining (`RewardPaymentDelay` = 2) assert_events_eq!( - Event::CollatorChosen { round: 12, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 12, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 12, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 12, collator_account: 4, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 12, + collator_account: 1, + total_exposed_amount: 50, + }, + Event::CollatorChosen { + round: 12, + collator_account: 2, + total_exposed_amount: 40, + }, + Event::CollatorChosen { + round: 12, + collator_account: 3, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 12, + collator_account: 4, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 55, round: 12, @@ -4261,10 +5874,22 @@ fn payouts_follow_delegation_changes() { ); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 2623 }, - Event::Rewarded { account: 7, rewards: 807 }, - Event::Rewarded { account: 10, rewards: 807 }, - Event::Rewarded { account: 8, rewards: 807 }, + Event::Rewarded { + account: 1, + rewards: 34, + }, + Event::Rewarded { + account: 7, + rewards: 10, + }, + Event::Rewarded { + account: 10, + rewards: 10, + }, + Event::Rewarded { + account: 8, + rewards: 10, + }, ); }); } @@ -4282,25 +5907,49 @@ fn bottom_delegations_are_empty_when_top_delegations_not_full() { assert!(top_delegations.delegations.is_empty()); assert!(bottom_delegations.delegations.is_empty()); // 1 delegator => 1 top delegator, 0 bottom delegators - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 10, + 10, + 10 + )); let top_delegations = ParachainStaking::top_delegations(1).unwrap(); let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); assert_eq!(top_delegations.delegations.len(), 1usize); assert!(bottom_delegations.delegations.is_empty()); // 2 delegators => 2 top delegators, 0 bottom delegators - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(3), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(3), + 1, + 10, + 10, + 10 + )); let top_delegations = ParachainStaking::top_delegations(1).unwrap(); let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); assert_eq!(top_delegations.delegations.len(), 2usize); assert!(bottom_delegations.delegations.is_empty()); // 3 delegators => 3 top delegators, 0 bottom delegators - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(4), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(4), + 1, + 10, + 10, + 10 + )); let top_delegations = ParachainStaking::top_delegations(1).unwrap(); let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); assert_eq!(top_delegations.delegations.len(), 3usize); assert!(bottom_delegations.delegations.is_empty()); // 4 delegators => 4 top delegators, 0 bottom delegators - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(5), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(5), + 1, + 10, + 10, + 10 + )); let top_delegations = ParachainStaking::top_delegations(1).unwrap(); let bottom_delegations = ParachainStaking::bottom_delegations(1).unwrap(); assert_eq!(top_delegations.delegations.len(), 4usize); @@ -4349,11 +5998,19 @@ fn candidate_pool_updates_when_total_counted_changes() { } // 15 + 16 + 17 + 18 + 20 = 86 (top 4 + self bond) is_candidate_pool_bond(1, 86); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(3), 1, 8)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(3), + 1, + 8 + )); // 3: 11 -> 19 => 3 is in top, bumps out 7 // 16 + 17 + 18 + 19 + 20 = 90 (top 4 + self bond) is_candidate_pool_bond(1, 90); - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(4), 1, 8)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(4), + 1, + 8 + )); // 4: 12 -> 20 => 4 is in top, bumps out 8 // 17 + 18 + 19 + 20 + 20 = 94 (top 4 + self bond) is_candidate_pool_bond(1, 94); @@ -4422,7 +6079,11 @@ fn only_top_collators_are_counted() { // 15 + 16 + 17 + 18 + 20 = 86 (top 4 + self bond) assert_eq!(collator_state.total_counted, 86); // bump bottom to the top - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(3), 1, 8)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(3), + 1, + 8 + )); assert_events_emitted!(Event::DelegationIncreased { delegator: 3, candidate: 1, @@ -4433,7 +6094,11 @@ fn only_top_collators_are_counted() { // 16 + 17 + 18 + 19 + 20 = 90 (top 4 + self bond) assert_eq!(collator_state.total_counted, 90); // bump bottom to the top - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(4), 1, 8)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(4), + 1, + 8 + )); assert_events_emitted!(Event::DelegationIncreased { delegator: 4, candidate: 1, @@ -4444,7 +6109,11 @@ fn only_top_collators_are_counted() { // 17 + 18 + 19 + 20 + 20 = 94 (top 4 + self bond) assert_eq!(collator_state.total_counted, 94); // bump bottom to the top - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(5), 1, 8)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(5), + 1, + 8 + )); assert_events_emitted!(Event::DelegationIncreased { delegator: 5, candidate: 1, @@ -4455,7 +6124,11 @@ fn only_top_collators_are_counted() { // 18 + 19 + 20 + 21 + 20 = 98 (top 4 + self bond) assert_eq!(collator_state.total_counted, 98); // bump bottom to the top - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(6), 1, 8)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(6), + 1, + 8 + )); assert_events_emitted!(Event::DelegationIncreased { delegator: 6, candidate: 1, @@ -4491,7 +6164,13 @@ fn delegation_events_convey_correct_position() { // 11 + 12 + 13 + 14 + 20 = 70 (top 4 + self bond) assert_eq!(collator1_state.total_counted, 70); // Top delegations are full, new highest delegation is made - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(7), 1, 15, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(7), + 1, + 15, + 10, + 10 + )); assert_events_emitted!(Event::Delegation { delegator: 7, locked_amount: 15, @@ -4503,7 +6182,13 @@ fn delegation_events_convey_correct_position() { // 12 + 13 + 14 + 15 + 20 = 70 (top 4 + self bond) assert_eq!(collator1_state.total_counted, 74); // New delegation is added to the bottom - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(8), + 1, + 10, + 10, + 10 + )); assert_events_emitted!(Event::Delegation { delegator: 8, locked_amount: 10, @@ -4515,7 +6200,11 @@ fn delegation_events_convey_correct_position() { // 12 + 13 + 14 + 15 + 20 = 70 (top 4 + self bond) assert_eq!(collator1_state.total_counted, 74); // 8 increases delegation to the top - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(8), 1, 3)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(8), + 1, + 3 + )); assert_events_emitted!(Event::DelegationIncreased { delegator: 8, candidate: 1, @@ -4526,7 +6215,11 @@ fn delegation_events_convey_correct_position() { // 13 + 13 + 14 + 15 + 20 = 75 (top 4 + self bond) assert_eq!(collator1_state.total_counted, 75); // 3 increases delegation but stays in bottom - assert_ok!(ParachainStaking::delegator_bond_more(RuntimeOrigin::signed(3), 1, 1)); + assert_ok!(ParachainStaking::delegator_bond_more( + RuntimeOrigin::signed(3), + 1, + 1 + )); assert_events_emitted!(Event::DelegationIncreased { delegator: 3, candidate: 1, @@ -4598,20 +6291,32 @@ fn no_rewards_paid_until_after_reward_payment_delay() { ExtBuilder::default() .with_balances(vec![(1, 20), (2, 20), (3, 20)]) .with_candidates(vec![(1, 20), (2, 20), (3, 20)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { - roll_to_round_begin(2); // payouts for round 1 set_author(1, 1, 1); set_author(1, 2, 1); set_author(1, 2, 1); set_author(1, 3, 1); set_author(1, 3, 1); + + roll_to_round_begin(2); assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 3, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 5, round: 2, @@ -4622,9 +6327,21 @@ fn no_rewards_paid_until_after_reward_payment_delay() { roll_to_round_begin(3); assert_events_eq!( - Event::CollatorChosen { round: 3, collator_account: 1, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 3, collator_account: 2, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 3, collator_account: 3, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 3, + collator_account: 1, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 3, + collator_account: 2, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 3, + collator_account: 3, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 10, round: 3, @@ -4634,13 +6351,22 @@ fn no_rewards_paid_until_after_reward_payment_delay() { ); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 3, rewards: 3 }); + assert_events_eq!(Event::Rewarded { + account: 3, + rewards: 1, + }); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 2 }); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 1, + }); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 2, rewards: 3 }); + assert_events_eq!(Event::Rewarded { + account: 2, + rewards: 1, + }); // there should be no more payments in this round... let num_blocks_rolled = roll_to_round_end(3); @@ -4659,7 +6385,6 @@ fn deferred_payment_storage_items_are_cleaned_up() { ExtBuilder::default() .with_balances(vec![(1, 20), (2, 20)]) .with_candidates(vec![(1, 20), (2, 20)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { set_author(1, 1, 1); @@ -4671,8 +6396,16 @@ fn deferred_payment_storage_items_are_cleaned_up() { roll_to_round_begin(2); assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 2, + collator_account: 1, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 2, + collator_account: 2, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 5, round: 2, @@ -4688,33 +6421,29 @@ fn deferred_payment_storage_items_are_cleaned_up() { assert!(>::contains_key(1, 1)); assert!(>::contains_key(1, 2)); - assert!( - !>::contains_key(1), - "DelayedPayouts shouldn't be populated until after RewardPaymentDelay" - ); assert!( >::contains_key(1), "Points should be populated during current round" ); - assert!( - >::contains_key(1), - "Staked should be populated when round changes" - ); assert!( !>::contains_key(2), "Points should not be populated until author noted" ); - assert!( - >::contains_key(2), - "Staked should be populated when round changes" - ); // first payout occurs in round 3 roll_to_round_begin(3); assert_events_eq!( - Event::CollatorChosen { round: 3, collator_account: 1, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 3, collator_account: 2, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 3, + collator_account: 1, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 3, + collator_account: 2, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 10, round: 3, @@ -4724,7 +6453,10 @@ fn deferred_payment_storage_items_are_cleaned_up() { ); roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 3 },); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 1, + },); // payouts should exist for past rounds that haven't been paid out yet.. assert!(>::contains_key(3, 1)); @@ -4737,18 +6469,17 @@ fn deferred_payment_storage_items_are_cleaned_up() { "DelayedPayouts should be populated after RewardPaymentDelay" ); assert!(>::contains_key(1)); + assert!(!>::contains_key(2)); assert!( - !>::contains_key(1), - "Staked should be cleaned up after round change" + !>::contains_key(2), + "We never rewarded points for round 2" ); - assert!(!>::contains_key(2)); - assert!(!>::contains_key(2), "We never rewarded points for round 2"); - assert!(>::contains_key(2)); - assert!(!>::contains_key(3)); - assert!(!>::contains_key(3), "We never awarded points for round 3"); - assert!(>::contains_key(3)); + assert!( + !>::contains_key(3), + "We never awarded points for round 3" + ); // collator 1 has been paid in this last block and associated storage cleaned up assert!(!>::contains_key(1, 1)); @@ -4760,12 +6491,23 @@ fn deferred_payment_storage_items_are_cleaned_up() { // second payout occurs in next block roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: 2, rewards: 3 },); + assert_events_eq!(Event::Rewarded { + account: 2, + rewards: 1, + },); roll_to_round_begin(4); assert_events_eq!( - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 20 }, + Event::CollatorChosen { + round: 4, + collator_account: 1, + total_exposed_amount: 20, + }, + Event::CollatorChosen { + round: 4, + collator_account: 2, + total_exposed_amount: 20, + }, Event::NewRound { starting_block: 15, round: 4, @@ -4777,7 +6519,6 @@ fn deferred_payment_storage_items_are_cleaned_up() { // collators have both been paid and storage fully cleaned up for round 1 assert!(!>::contains_key(1, 2)); assert!(!>::contains_key(1, 2)); - assert!(!>::contains_key(1)); assert!(!>::contains_key(1)); // points should be cleaned up assert!(!>::contains_key(1)); @@ -4812,7 +6553,6 @@ fn deferred_payment_and_at_stake_storage_items_cleaned_up_for_candidates_not_pro assert!(>::contains_key(1, 1)); assert!(>::contains_key(1, 2)); assert!(!>::contains_key(1, 3)); - assert!(>::contains_key(1)); assert!(>::contains_key(1)); roll_to_round_begin(3); assert!(>::contains_key(1)); @@ -4825,7 +6565,6 @@ fn deferred_payment_and_at_stake_storage_items_cleaned_up_for_candidates_not_pro assert!(!>::contains_key(1, 1)); assert!(!>::contains_key(1, 2)); assert!(!>::contains_key(1, 3)); - assert!(!>::contains_key(1)); assert!(!>::contains_key(1)); assert!(!>::contains_key(1)); }); @@ -4869,7 +6608,6 @@ fn deferred_payment_steady_state_event_flow() { (44, 4, 100), (44, 1, 100), ]) - .with_rewards_account(999, 100000) .build() .execute_with(|| { // convenience to set the round points consistently @@ -4914,16 +6652,35 @@ fn deferred_payment_steady_state_event_flow() { // returns new round index let roll_through_steady_state_round = |round: BlockNumber| -> BlockNumber { let num_rounds_rolled = roll_to_round_begin(round); - assert!(num_rounds_rolled <= 1, "expected to be at round begin already"); + assert!( + num_rounds_rolled <= 1, + "expected to be at round begin already" + ); assert_events_eq!( - Event::CollatorChosen { round, collator_account: 1, total_exposed_amount: 400 }, - Event::CollatorChosen { round, collator_account: 2, total_exposed_amount: 400 }, - Event::CollatorChosen { round, collator_account: 3, total_exposed_amount: 400 }, - Event::CollatorChosen { round, collator_account: 4, total_exposed_amount: 400 }, + Event::CollatorChosen { + round: round as u32, + collator_account: 1, + total_exposed_amount: 400, + }, + Event::CollatorChosen { + round: round as u32, + collator_account: 2, + total_exposed_amount: 400, + }, + Event::CollatorChosen { + round: round as u32, + collator_account: 3, + total_exposed_amount: 400, + }, + Event::CollatorChosen { + round: round as u32, + collator_account: 4, + total_exposed_amount: 400, + }, Event::NewRound { - starting_block: (round as u64 - 1) * 5, - round, + starting_block: (round as u32 - 1) * 5, + round: round as u32, selected_collators_number: 4, total_balance: 1600, }, @@ -4933,30 +6690,66 @@ fn deferred_payment_steady_state_event_flow() { roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 3, rewards: 769 }, - Event::Rewarded { account: 22, rewards: 256 }, - Event::Rewarded { account: 33, rewards: 256 }, + Event::Rewarded { + account: 3, + rewards: 19, + }, + Event::Rewarded { + account: 22, + rewards: 6, + }, + Event::Rewarded { + account: 33, + rewards: 6, + }, ); roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 4, rewards: 769 }, - Event::Rewarded { account: 33, rewards: 256 }, - Event::Rewarded { account: 44, rewards: 256 }, + Event::Rewarded { + account: 4, + rewards: 19, + }, + Event::Rewarded { + account: 33, + rewards: 6, + }, + Event::Rewarded { + account: 44, + rewards: 6, + }, ); roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 769 }, - Event::Rewarded { account: 11, rewards: 256 }, - Event::Rewarded { account: 44, rewards: 256 }, + Event::Rewarded { + account: 1, + rewards: 19, + }, + Event::Rewarded { + account: 11, + rewards: 6, + }, + Event::Rewarded { + account: 44, + rewards: 6, + }, ); roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 2, rewards: 769 }, - Event::Rewarded { account: 11, rewards: 256 }, - Event::Rewarded { account: 22, rewards: 256 }, + Event::Rewarded { + account: 2, + rewards: 19, + }, + Event::Rewarded { + account: 11, + rewards: 6, + }, + Event::Rewarded { + account: 22, + rewards: 6, + }, ); roll_blocks(1); @@ -5011,9 +6804,18 @@ fn delegation_kicked_from_bottom_removes_pending_request() { ]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); // 10 delegates to full 1 => kicks lowest delegation (2, 19) - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(10), 1, 20, 8, 0)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(10), + 1, + 20, + 8, + 0 + )); // check the event assert_events_emitted!(Event::DelegationKicked { delegator: 2, @@ -5021,7 +6823,7 @@ fn delegation_kicked_from_bottom_removes_pending_request() { unstaked_amount: 19, }); // ensure request DNE - assert!(!ParachainStaking::delegation_scheduled_requests(1) + assert!(!ParachainStaking::delegation_scheduled_requests(&1) .iter() .any(|x| x.delegator == 2)); }); @@ -5063,8 +6865,13 @@ fn no_selected_candidates_defaults_to_last_round_collators() { // check AtStake matches previous let new_selected_candidates = ParachainStaking::selected_candidates(); assert_eq!(old_selected_candidates, new_selected_candidates); - for (index, account) in new_selected_candidates.into_iter().enumerate() { - assert_eq!(old_at_stake_snapshots[index], >::get(new_round, account)); + let mut index = 0usize; + for account in new_selected_candidates { + assert_eq!( + old_at_stake_snapshots[index], + >::get(new_round, account) + ); + index += 1usize; } }); } @@ -5075,13 +6882,15 @@ fn test_delegator_scheduled_for_revoke_is_rewarded_for_previous_rounds_but_not_f .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { // preset rewards for rounds 1, 2 and 3 (1..=3).for_each(|round| set_author(round, 1, 1)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_events_eq!(Event::DelegationRevocationScheduled { round: 1, delegator: 2, @@ -5093,20 +6902,32 @@ fn test_delegator_scheduled_for_revoke_is_rewarded_for_previous_rounds_but_not_f 1, collator.delegation_count, "collator's delegator count was reduced unexpectedly" ); - assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + assert_eq!( + 30, collator.total_counted, + "collator's total was reduced unexpectedly" + ); roll_to_round_begin(3); assert_events_emitted_match!(Event::NewRound { round: 3, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 7 }, - Event::Rewarded { account: 2, rewards: 3 }, + Event::Rewarded { + account: 1, + rewards: 4, + }, + Event::Rewarded { + account: 2, + rewards: 1, + }, ); roll_to_round_begin(4); assert_events_emitted_match!(Event::NewRound { round: 4, .. }); roll_blocks(3); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 10 },); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 5, + },); let collator_snapshot = ParachainStaking::at_stake(ParachainStaking::round().current, 1) .unwrap_or_default(); @@ -5125,16 +6946,18 @@ fn test_delegator_scheduled_for_revoke_is_rewarded_for_previous_rounds_but_not_f #[test] fn test_delegator_scheduled_for_revoke_is_rewarded_when_request_cancelled() { ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 30), (3, 20), (4, 20)]) + .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { // preset rewards for rounds 2, 3 and 4 (2..=4).for_each(|round| set_author(round, 1, 1)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_events_eq!(Event::DelegationRevocationScheduled { round: 1, delegator: 2, @@ -5146,15 +6969,24 @@ fn test_delegator_scheduled_for_revoke_is_rewarded_when_request_cancelled() { 1, collator.delegation_count, "collator's delegator count was reduced unexpectedly" ); - assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + assert_eq!( + 30, collator.total_counted, + "collator's total was reduced unexpectedly" + ); roll_to_round_begin(2); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1 + )); roll_to_round_begin(4); assert_events_emitted_match!(Event::NewRound { round: 4, .. }); roll_blocks(3); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 9 },); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 5, + },); let collator_snapshot = ParachainStaking::at_stake(ParachainStaking::round().current, 1) .unwrap_or_default(); @@ -5172,8 +7004,14 @@ fn test_delegator_scheduled_for_revoke_is_rewarded_when_request_cancelled() { assert_events_emitted_match!(Event::NewRound { round: 5, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 7 }, - Event::Rewarded { account: 2, rewards: 2 }, + Event::Rewarded { + account: 1, + rewards: 4, + }, + Event::Rewarded { + account: 2, + rewards: 1, + }, ); }); } @@ -5185,7 +7023,6 @@ fn test_delegator_scheduled_for_bond_decrease_is_rewarded_for_previous_rounds_bu .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) .with_delegations(vec![(2, 1, 20), (2, 3, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { // preset rewards for rounds 1, 2 and 3 @@ -5207,22 +7044,37 @@ fn test_delegator_scheduled_for_bond_decrease_is_rewarded_for_previous_rounds_bu 1, collator.delegation_count, "collator's delegator count was reduced unexpectedly" ); - assert_eq!(40, collator.total_counted, "collator's total was reduced unexpectedly"); + assert_eq!( + 40, collator.total_counted, + "collator's total was reduced unexpectedly" + ); roll_to_round_begin(3); assert_events_emitted_match!(Event::NewRound { round: 3, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 6 }, - Event::Rewarded { account: 2, rewards: 4 }, + Event::Rewarded { + account: 1, + rewards: 3, + }, + Event::Rewarded { + account: 2, + rewards: 2, + }, ); roll_to_round_begin(4); assert_events_emitted_match!(Event::NewRound { round: 4, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 7 }, - Event::Rewarded { account: 2, rewards: 3 }, + Event::Rewarded { + account: 1, + rewards: 4, + }, + Event::Rewarded { + account: 2, + rewards: 1, + }, ); let collator_snapshot = ParachainStaking::at_stake(ParachainStaking::round().current, 1) @@ -5245,7 +7097,6 @@ fn test_delegator_scheduled_for_bond_decrease_is_rewarded_when_request_cancelled .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) .with_delegations(vec![(2, 1, 20), (2, 3, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { // preset rewards for rounds 2, 3 and 4 @@ -5267,17 +7118,29 @@ fn test_delegator_scheduled_for_bond_decrease_is_rewarded_when_request_cancelled 1, collator.delegation_count, "collator's delegator count was reduced unexpectedly" ); - assert_eq!(40, collator.total_counted, "collator's total was reduced unexpectedly"); + assert_eq!( + 40, collator.total_counted, + "collator's total was reduced unexpectedly" + ); roll_to_round_begin(2); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1 + )); roll_to_round_begin(4); assert_events_emitted_match!(Event::NewRound { round: 4, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 7 }, - Event::Rewarded { account: 2, rewards: 3 }, + Event::Rewarded { + account: 1, + rewards: 4, + }, + Event::Rewarded { + account: 2, + rewards: 1, + }, ); let collator_snapshot = ParachainStaking::at_stake(ParachainStaking::round().current, 1) @@ -5296,8 +7159,14 @@ fn test_delegator_scheduled_for_bond_decrease_is_rewarded_when_request_cancelled assert_events_emitted_match!(Event::NewRound { round: 5, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 6 }, - Event::Rewarded { account: 2, rewards: 4 }, + Event::Rewarded { + account: 1, + rewards: 3, + }, + Event::Rewarded { + account: 2, + rewards: 2, + }, ); }); } @@ -5308,14 +7177,19 @@ fn test_delegator_scheduled_for_leave_is_rewarded_for_previous_rounds_but_not_fo .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { // preset rewards for rounds 1, 2 and 3 (1..=3).for_each(|round| set_author(round, 1, 1)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3, + )); assert_events_eq!( Event::DelegationRevocationScheduled { round: 1, @@ -5335,20 +7209,32 @@ fn test_delegator_scheduled_for_leave_is_rewarded_for_previous_rounds_but_not_fo 1, collator.delegation_count, "collator's delegator count was reduced unexpectedly" ); - assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + assert_eq!( + 30, collator.total_counted, + "collator's total was reduced unexpectedly" + ); roll_to_round_begin(3); assert_events_emitted_match!(Event::NewRound { round: 3, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 7 }, - Event::Rewarded { account: 2, rewards: 3 }, + Event::Rewarded { + account: 1, + rewards: 4, + }, + Event::Rewarded { + account: 2, + rewards: 1, + }, ); roll_to_round_begin(4); assert_events_emitted_match!(Event::NewRound { round: 4, .. }); roll_blocks(3); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 10 },); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 5, + },); let collator_snapshot = ParachainStaking::at_stake(ParachainStaking::round().current, 1) .unwrap_or_default(); @@ -5370,14 +7256,19 @@ fn test_delegator_scheduled_for_leave_is_rewarded_when_request_cancelled() { .with_balances(vec![(1, 20), (2, 40), (3, 20), (4, 20)]) .with_candidates(vec![(1, 20), (3, 20), (4, 20)]) .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { // preset rewards for rounds 2, 3 and 4 (2..=4).for_each(|round| set_author(round, 1, 1)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3, + )); assert_events_eq!( Event::DelegationRevocationScheduled { round: 1, @@ -5397,16 +7288,28 @@ fn test_delegator_scheduled_for_leave_is_rewarded_when_request_cancelled() { 1, collator.delegation_count, "collator's delegator count was reduced unexpectedly" ); - assert_eq!(30, collator.total_counted, "collator's total was reduced unexpectedly"); + assert_eq!( + 30, collator.total_counted, + "collator's total was reduced unexpectedly" + ); roll_to_round_begin(2); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 1,)); - assert_ok!(ParachainStaking::cancel_delegation_request(RuntimeOrigin::signed(2), 3,)); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1, + )); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 3, + )); roll_to_round_begin(4); assert_events_emitted_match!(Event::NewRound { round: 4, .. }); roll_blocks(3); - assert_events_eq!(Event::Rewarded { account: 1, rewards: 10 },); + assert_events_eq!(Event::Rewarded { + account: 1, + rewards: 5, + },); let collator_snapshot = ParachainStaking::at_stake(ParachainStaking::round().current, 1) .unwrap_or_default(); @@ -5424,8 +7327,14 @@ fn test_delegator_scheduled_for_leave_is_rewarded_when_request_cancelled() { assert_events_emitted_match!(Event::NewRound { round: 5, .. }); roll_blocks(3); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 7 }, - Event::Rewarded { account: 2, rewards: 3 }, + Event::Rewarded { + account: 1, + rewards: 4, + }, + Event::Rewarded { + account: 2, + rewards: 1, + }, ); }); } @@ -5548,10 +7457,12 @@ fn test_hotfix_remove_delegation_requests_exited_candidates_cleans_up() { // invalid state >::insert(2, BoundedVec::default()); >::insert(3, BoundedVec::default()); - assert_ok!(ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( - RuntimeOrigin::signed(1), - vec![2, 3, 4] // 4 does not exist, but is OK for idempotency - )); + assert_ok!( + ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( + RuntimeOrigin::signed(1), + vec![2, 3, 4] // 4 does not exist, but is OK for idempotency + ) + ); assert!(!>::contains_key(2)); assert!(!>::contains_key(3)); @@ -5568,10 +7479,12 @@ fn test_hotfix_remove_delegation_requests_exited_candidates_cleans_up_only_speci // invalid state >::insert(2, BoundedVec::default()); >::insert(3, BoundedVec::default()); - assert_ok!(ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( - RuntimeOrigin::signed(1), - vec![2] - )); + assert_ok!( + ParachainStaking::hotfix_remove_delegation_requests_exited_candidates( + RuntimeOrigin::signed(1), + vec![2] + ) + ); assert!(!>::contains_key(2)); assert!(>::contains_key(3)); @@ -5633,16 +7546,22 @@ fn locking_zero_amount_removes_lock() { // this test demonstrates the behavior of pallet Balance's `LockableCurrency` implementation of // `set_locks()` when an amount of 0 is provided: any previous lock is removed - ExtBuilder::default().with_balances(vec![(1, 100)]).build().execute_with(|| { - assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), None); + ExtBuilder::default() + .with_balances(vec![(1, 100)]) + .build() + .execute_with(|| { + assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), None); - Balances::set_lock(DELEGATOR_LOCK_ID, &1, 1, WithdrawReasons::all()); - assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), Some(1)); + Balances::set_lock(DELEGATOR_LOCK_ID, &1, 1, WithdrawReasons::all()); + assert_eq!( + crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), + Some(1) + ); - Balances::set_lock(DELEGATOR_LOCK_ID, &1, 0, WithdrawReasons::all()); - // Note that we tried to call `set_lock(0)` and the previous lock gets removed - assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), None); - }); + Balances::set_lock(DELEGATOR_LOCK_ID, &1, 0, WithdrawReasons::all()); + // Note that we tried to call `set_lock(0)` and the previous lock gets removed + assert_eq!(crate::mock::query_lock_amount(1, DELEGATOR_LOCK_ID), None); + }); } #[test] @@ -5653,20 +7572,32 @@ fn revoke_last_removes_lock() { .with_delegations(vec![(3, 1, 30), (3, 2, 25)]) .build() .execute_with(|| { - assert_eq!(crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), Some(55)); + assert_eq!( + crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), + Some(55) + ); // schedule and remove one... - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(3), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(3), + 1 + )); roll_to_round_begin(3); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(3), 3, 1 )); - assert_eq!(crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), Some(25)); + assert_eq!( + crate::mock::query_lock_amount(3, DELEGATOR_LOCK_ID), + Some(25) + ); // schedule and remove the other... - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(3), 2)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(3), + 2 + )); roll_to_round_begin(5); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(3), @@ -5710,9 +7641,12 @@ fn test_set_auto_compound_fails_if_invalid_candidate_auto_compounding_hint() { .build() .execute_with(|| { >::new( - vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(10) }] - .try_into() - .expect("must succeed"), + vec![AutoCompoundConfig { + delegator: 2, + value: Percent::from_percent(10), + }] + .try_into() + .expect("must succeed"), ) .set_storage(&1); let candidate_auto_compounding_delegation_count_hint = 0; // is however, 1 @@ -5752,8 +7686,11 @@ fn test_set_auto_compound_inserts_if_not_exists() { value: Percent::from_percent(50), }); assert_eq!( - vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(50) }], - ParachainStaking::auto_compounding_delegations(1).into_inner(), + vec![AutoCompoundConfig { + delegator: 2, + value: Percent::from_percent(50), + }], + ParachainStaking::auto_compounding_delegations(&1).into_inner(), ); }); } @@ -5767,9 +7704,12 @@ fn test_set_auto_compound_updates_if_existing() { .build() .execute_with(|| { >::new( - vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(10) }] - .try_into() - .expect("must succeed"), + vec![AutoCompoundConfig { + delegator: 2, + value: Percent::from_percent(10), + }] + .try_into() + .expect("must succeed"), ) .set_storage(&1); @@ -5786,8 +7726,11 @@ fn test_set_auto_compound_updates_if_existing() { value: Percent::from_percent(50), }); assert_eq!( - vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(50) }], - ParachainStaking::auto_compounding_delegations(1).into_inner(), + vec![AutoCompoundConfig { + delegator: 2, + value: Percent::from_percent(50), + }], + ParachainStaking::auto_compounding_delegations(&1).into_inner(), ); }); } @@ -5801,9 +7744,12 @@ fn test_set_auto_compound_removes_if_auto_compound_zero_percent() { .build() .execute_with(|| { >::new( - vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(10) }] - .try_into() - .expect("must succeed"), + vec![AutoCompoundConfig { + delegator: 2, + value: Percent::from_percent(10), + }] + .try_into() + .expect("must succeed"), ) .set_storage(&1); @@ -5819,7 +7765,7 @@ fn test_set_auto_compound_removes_if_auto_compound_zero_percent() { delegator: 2, value: Percent::zero(), }); - assert_eq!(0, ParachainStaking::auto_compounding_delegations(1).len(),); + assert_eq!(0, ParachainStaking::auto_compounding_delegations(&1).len(),); }); } @@ -5845,7 +7791,10 @@ fn test_execute_revoke_delegation_removes_auto_compounding_from_state_for_delega 0, 2, )); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -5853,13 +7802,13 @@ fn test_execute_revoke_delegation_removes_auto_compounding_from_state_for_delega 1 )); assert!( - !ParachainStaking::auto_compounding_delegations(1) + !ParachainStaking::auto_compounding_delegations(&1) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was not removed" ); assert!( - ParachainStaking::auto_compounding_delegations(3) + ParachainStaking::auto_compounding_delegations(&3) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was erroneously removed" @@ -5890,8 +7839,14 @@ fn test_execute_leave_delegators_removes_auto_compounding_state() { 2, )); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, + )); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3, + )); roll_to(10); assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), @@ -5905,13 +7860,13 @@ fn test_execute_leave_delegators_removes_auto_compounding_state() { )); assert!( - !ParachainStaking::auto_compounding_delegations(1) + !ParachainStaking::auto_compounding_delegations(&1) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was not removed" ); assert!( - !ParachainStaking::auto_compounding_delegations(3) + !ParachainStaking::auto_compounding_delegations(&3) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was not removed" @@ -5942,18 +7897,25 @@ fn test_execute_leave_candidates_removes_auto_compounding_state() { 2, )); - assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1), 2)); + assert_ok!(ParachainStaking::schedule_leave_candidates( + RuntimeOrigin::signed(1), + 2 + )); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1, 1,)); + assert_ok!(ParachainStaking::execute_leave_candidates( + RuntimeOrigin::signed(1), + 1, + 1, + )); assert!( - !ParachainStaking::auto_compounding_delegations(1) + !ParachainStaking::auto_compounding_delegations(&1) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was not removed" ); assert!( - ParachainStaking::auto_compounding_delegations(3) + ParachainStaking::auto_compounding_delegations(&3) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was erroneously removed" @@ -6000,10 +7962,16 @@ fn test_delegation_kicked_from_bottom_delegation_removes_auto_compounding_state( )); // kicks lowest delegation (2, 19) - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(10), 1, 20, 8, 0)); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(10), + 1, + 20, + 8, + 0 + )); assert!( - !ParachainStaking::auto_compounding_delegations(1) + !ParachainStaking::auto_compounding_delegations(&1) .iter() .any(|x| x.delegator == 2), "delegation auto-compound config was not removed" @@ -6017,7 +7985,6 @@ fn test_rewards_do_not_auto_compound_on_payment_if_delegation_scheduled_revoke_e .with_balances(vec![(1, 100), (2, 200), (3, 200)]) .with_candidates(vec![(1, 100)]) .with_delegations(vec![(2, 1, 200), (3, 1, 200)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { (2..=5).for_each(|round| set_author(round, 1, 1)); @@ -6038,11 +8005,18 @@ fn test_rewards_do_not_auto_compound_on_payment_if_delegation_scheduled_revoke_e roll_to_round_begin(3); // schedule revoke for delegator 2; no rewards should be compounded - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); roll_to_round_begin(4); assert_events_eq!( - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 500 }, + Event::CollatorChosen { + round: 4, + collator_account: 1, + total_exposed_amount: 500, + }, Event::NewRound { starting_block: 15, round: 4, @@ -6053,12 +8027,25 @@ fn test_rewards_do_not_auto_compound_on_payment_if_delegation_scheduled_revoke_e roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 11 }, + Event::Rewarded { + account: 1, + rewards: 9, + }, // no compound since revoke request exists - Event::Rewarded { account: 2, rewards: 10 }, + Event::Rewarded { + account: 2, + rewards: 8, + }, // 50% - Event::Rewarded { account: 3, rewards: 10 }, - Event::Compounded { candidate: 1, delegator: 3, amount: 5 }, + Event::Rewarded { + account: 3, + rewards: 8, + }, + Event::Compounded { + candidate: 1, + delegator: 3, + amount: 4, + }, ); }); } @@ -6069,7 +8056,6 @@ fn test_rewards_auto_compound_on_payment_as_per_auto_compound_config() { .with_balances(vec![(1, 100), (2, 200), (3, 200), (4, 200), (5, 200)]) .with_candidates(vec![(1, 100)]) .with_delegations(vec![(2, 1, 200), (3, 1, 200), (4, 1, 200), (5, 1, 200)]) - .with_rewards_account(999, 100) .build() .execute_with(|| { (2..=6).for_each(|round| set_author(round, 1, 1)); @@ -6097,7 +8083,11 @@ fn test_rewards_auto_compound_on_payment_as_per_auto_compound_config() { roll_to_round_begin(4); assert_events_eq!( - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 900 }, + Event::CollatorChosen { + round: 4, + collator_account: 1, + total_exposed_amount: 900, + }, Event::NewRound { starting_block: 15, round: 4, @@ -6108,17 +8098,40 @@ fn test_rewards_auto_compound_on_payment_as_per_auto_compound_config() { roll_blocks(1); assert_events_eq!( - Event::Rewarded { account: 1, rewards: 14 }, + Event::Rewarded { + account: 1, + rewards: 13, + }, // 0% - Event::Rewarded { account: 2, rewards: 9 }, + Event::Rewarded { + account: 2, + rewards: 8, + }, // 50% - Event::Rewarded { account: 3, rewards: 9 }, - Event::Compounded { candidate: 1, delegator: 3, amount: 5 }, + Event::Rewarded { + account: 3, + rewards: 8, + }, + Event::Compounded { + candidate: 1, + delegator: 3, + amount: 4, + }, // 100% - Event::Rewarded { account: 4, rewards: 9 }, - Event::Compounded { candidate: 1, delegator: 4, amount: 9 }, + Event::Rewarded { + account: 4, + rewards: 8, + }, + Event::Compounded { + candidate: 1, + delegator: 4, + amount: 8, + }, // no-config - Event::Rewarded { account: 5, rewards: 9 }, + Event::Rewarded { + account: 5, + rewards: 8, + }, ); }); } @@ -6228,8 +8241,11 @@ fn test_delegate_with_auto_compound_sets_auto_compound_config() { auto_compound: Percent::from_percent(50), }); assert_eq!( - vec![AutoCompoundConfig { delegator: 2, value: Percent::from_percent(50) }], - ParachainStaking::auto_compounding_delegations(1).into_inner(), + vec![AutoCompoundConfig { + delegator: 2, + value: Percent::from_percent(50), + }], + ParachainStaking::auto_compounding_delegations(&1).into_inner(), ); }); } @@ -6250,7 +8266,7 @@ fn test_delegate_with_auto_compound_skips_storage_but_emits_event_for_zero_auto_ 0, 0, )); - assert_eq!(0, ParachainStaking::auto_compounding_delegations(1).len(),); + assert_eq!(0, ParachainStaking::auto_compounding_delegations(&1).len(),); assert_events_eq!(Event::Delegation { delegator: 2, locked_amount: 10, @@ -6268,7 +8284,7 @@ fn test_delegate_with_auto_compound_reserves_balance() { .with_candidates(vec![(1, 30)]) .build() .execute_with(|| { - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 10); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 10); assert_ok!(ParachainStaking::delegate_with_auto_compound( RuntimeOrigin::signed(2), 1, @@ -6278,7 +8294,7 @@ fn test_delegate_with_auto_compound_reserves_balance() { 0, 0, )); - assert_eq!(ParachainStaking::get_delegator_stakable_free_balance(&2), 0); + assert_eq!(ParachainStaking::get_delegator_stakable_balance(&2), 0); }); } @@ -6347,7 +8363,11 @@ fn test_delegate_with_auto_compound_can_delegate_immediately_after_other_join_ca .with_balances(vec![(1, 20), (2, 20)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::join_candidates(RuntimeOrigin::signed(1), 20, 0)); + assert_ok!(ParachainStaking::join_candidates( + RuntimeOrigin::signed(1), + 20, + 0 + )); assert_ok!(ParachainStaking::delegate_with_auto_compound( RuntimeOrigin::signed(2), 1, @@ -6368,7 +8388,10 @@ fn test_delegate_with_auto_compound_can_delegate_to_other_if_revoking() { .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1 + )); assert_ok!(ParachainStaking::delegate_with_auto_compound( RuntimeOrigin::signed(2), 4, @@ -6468,7 +8491,10 @@ fn test_delegate_with_auto_compound_can_delegate_if_greater_than_lowest_bottom() candidate: 1, unstaked_amount: 10 }); - assert_events_emitted!(Event::DelegatorLeft { delegator: 10, unstaked_amount: 10 }); + assert_events_emitted!(Event::DelegatorLeft { + delegator: 10, + unstaked_amount: 10 + }); }); } @@ -6480,7 +8506,10 @@ fn test_delegate_with_auto_compound_can_still_delegate_to_other_if_leaving() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1,)); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, + )); assert_ok!(ParachainStaking::delegate_with_auto_compound( RuntimeOrigin::signed(2), 3, @@ -6571,8 +8600,14 @@ fn test_delegate_skips_auto_compound_storage_but_emits_event_for_zero_auto_compo .execute_with(|| { // We already have an auto-compounding delegation from 3 -> 1, so the hint validation // would cause a failure if the auto-compounding isn't skipped properly. - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(2), 1, 10, 1, 0,)); - assert_eq!(1, ParachainStaking::auto_compounding_delegations(1).len(),); + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(2), + 1, + 10, + 1, + 0, + )); + assert_eq!(1, ParachainStaking::auto_compounding_delegations(&1).len(),); assert_events_eq!(Event::Delegation { delegator: 2, locked_amount: 10, @@ -6585,11 +8620,9 @@ fn test_delegate_skips_auto_compound_storage_but_emits_event_for_zero_auto_compo #[test] fn test_on_initialize_weights() { - use crate::{ - mock::System, - weights::{SubstrateWeight as PalletWeights, WeightInfo}, - *, - }; + use crate::mock::System; + use crate::weights::{SubstrateWeight as PalletWeights, WeightInfo}; + use crate::*; use frame_support::{pallet_prelude::*, weights::constants::RocksDbWeight}; // generate balance, candidate, and delegation vecs to "fill" out delegations @@ -6616,12 +8649,11 @@ fn test_on_initialize_weights() { let weight = ParachainStaking::on_initialize(1); // TODO: build this with proper db reads/writes - assert_eq!(Weight::from_parts(331000000, 1832), weight); + assert_eq!(Weight::from_parts(277168000, 0), weight); // roll to the end of the round, then run on_init again, we should see round change... + set_author(3, 1, 100); // must set some points for prepare_staking_payouts roll_to_round_end(3); - set_author(2, 1, 100); // must set some points for prepare_staking_payouts - System::set_block_number(System::block_number() + 1); let block = System::block_number() + 1; let weight = ParachainStaking::on_initialize(block); @@ -6631,8 +8663,8 @@ fn test_on_initialize_weights() { // // following this assertion, we add individual weights together to show that we can // derive this number independently. - let expected_on_init = 2222282921; - assert_eq!(Weight::from_parts(expected_on_init, 40062), weight); + let expected_on_init = 2404547135; + assert_eq!(Weight::from_parts(expected_on_init, 32562), weight); // assemble weight manually to ensure it is well understood let mut expected_weight = 0u64; @@ -6649,12 +8681,12 @@ fn test_on_initialize_weights() { .ref_time(); // SlotProvider read expected_weight += RocksDbWeight::get().reads_writes(1, 0).ref_time(); - // Round and Staked writes, done in on-round-change code block inside on_initialize() - expected_weight += RocksDbWeight::get().reads_writes(0, 2).ref_time(); + // Round write, done in on-round-change code block inside on_initialize() + expected_weight += RocksDbWeight::get().reads_writes(0, 1).ref_time(); // more reads/writes manually accounted for for on_finalize expected_weight += RocksDbWeight::get().reads_writes(3, 2).ref_time(); - assert_eq!(Weight::from_parts(expected_weight, 40062), weight); + assert_eq!(Weight::from_parts(expected_weight, 32562), weight); assert_eq!(expected_on_init, expected_weight); // magic number == independent accounting }); } @@ -6670,7 +8702,10 @@ fn test_compute_top_candidates_is_stable() { assert_eq!(ParachainStaking::candidate_pool().0.len(), 6); assert_eq!(ParachainStaking::total_selected(), 5); // Returns the 5 candidates with greater AccountId, because they are iterated in reverse - assert_eq!(ParachainStaking::compute_top_candidates(), vec![2, 3, 4, 5, 6]); + assert_eq!( + ParachainStaking::compute_top_candidates(), + vec![2, 3, 4, 5, 6] + ); }); } @@ -6691,145 +8726,3 @@ fn test_removed_calls() { ); }); } - -#[test] -fn rewards_should_be_constant_when_annual_range_is_fix() { - let collator = 2; - ExtBuilder::default() - .with_balances(vec![(collator, 30)]) - .with_candidates(vec![(collator, 30)]) - .with_rewards_account(10, 1000000000) - .with_inflation(InflationInfo { - expect: Range { min: 0, ideal: 0, max: 0 }, - annual: Range { - min: Perbill::from_perthousand(75), - ideal: Perbill::from_perthousand(75), - max: Perbill::from_perthousand(75), - }, - round: Range { min: Perbill::zero(), ideal: Perbill::zero(), max: Perbill::zero() }, - }) - .build() - .execute_with(|| { - let rewards_delay = mock::RewardPaymentDelay::get(); - - // let's check the first 100 rounds - for i in 1..=100 { - set_author(i, collator, 100); - roll_to_round_begin(i + rewards_delay); - roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: collator, rewards: 71 }); - } - }); -} - -#[test] -fn collator_rewards_consistency_over_fixed_annual_range() { - let col = 2; - let col_1 = 3; - let col_stake = 30; - let col_1_stake = 30; - ExtBuilder::default() - .with_balances(vec![(col, 30), (col_1, 30)]) - .with_candidates(vec![(col, col_stake), (col_1, col_1_stake)]) - .build() - .execute_with(|| { - // check the blocks per round - let blocks_per_round = ParachainStaking::round().length; - assert_eq!(blocks_per_round, 5); - - for round in 2..=103 { - // rolling to check the rewards - roll_to_round_begin(round); - assert_events_eq!( - Event::CollatorChosen { - round, - collator_account: col, - total_exposed_amount: col_stake, - }, - Event::CollatorChosen { - round, - collator_account: col_1, - total_exposed_amount: col_1_stake - }, - Event::NewRound { - starting_block: (blocks_per_round * (round - 1)).into(), - round, - selected_collators_number: 2, - total_balance: col_stake + col_1_stake, - }, - ); - } - }); -} - -#[test] -fn rewards_with_2_collators() { - let col = 2; - let col_1 = 3; - let col_stake = 30; - let col_1_stake = 30; - ExtBuilder::default() - .with_balances(vec![(col, 30), (col_1, 30)]) - .with_candidates(vec![(col, col_stake), (col_1, col_1_stake)]) - .with_rewards_account(10, 1000000000) - .with_inflation(InflationInfo { - expect: Range { min: 0, ideal: 0, max: 0 }, - annual: Range { - min: Perbill::from_perthousand(75), - ideal: Perbill::from_perthousand(75), - max: Perbill::from_perthousand(75), - }, - round: Range { min: Perbill::zero(), ideal: Perbill::zero(), max: Perbill::zero() }, - }) - .build() - .execute_with(|| { - let rewards_delay = mock::RewardPaymentDelay::get(); - - // check the blocks per round - let blocks_per_round = ParachainStaking::round().length; - assert_eq!(blocks_per_round, 5); - - let round = 1; - set_author(round, col, 100); - let round = round + rewards_delay; - roll_to_round_begin(round); - roll_blocks(1); - assert_no_events!(); - roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: col, rewards: 71 },); - roll_blocks(1); - assert_no_events!(); - roll_blocks(1); - assert_no_events!(); - - // same points - let round = 5; - set_author(round, col, 100); - set_author(round, col_1, 100); - let round = round + rewards_delay; - roll_to_round_begin(round); - roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: col_1, rewards: 35 },); - roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: col, rewards: 35 },); - roll_blocks(1); - assert_no_events!(); - roll_blocks(1); - assert_no_events!(); - - // same points - let round = 10; - set_author(round, col, 200); - set_author(round, col_1, 100); - let round = round + rewards_delay; - roll_to_round_begin(round); - roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: col_1, rewards: 24 },); - roll_blocks(1); - assert_events_eq!(Event::Rewarded { account: col, rewards: 47 },); - roll_blocks(1); - assert_no_events!(); - roll_blocks(1); - assert_no_events!(); - }); -} diff --git a/pallets/parachain-staking/src/traits.rs b/pallets/parachain-staking/src/traits.rs index a6fb0927b..0300443b9 100644 --- a/pallets/parachain-staking/src/traits.rs +++ b/pallets/parachain-staking/src/traits.rs @@ -1,18 +1,18 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! traits for parachain-staking @@ -78,6 +78,8 @@ impl OnInactiveCollator for () { _round: crate::RoundIndex, ) -> Result> { crate::Pallet::::go_offline_inner(collator_id)?; - Ok(::WeightInfo::go_offline(crate::MAX_CANDIDATES)) + Ok(::WeightInfo::go_offline( + crate::MAX_CANDIDATES, + )) } } diff --git a/pallets/parachain-staking/src/types.rs b/pallets/parachain-staking/src/types.rs index ae98ec805..d2dcbb292 100644 --- a/pallets/parachain-staking/src/types.rs +++ b/pallets/parachain-staking/src/types.rs @@ -1,18 +1,18 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . //! Types for parachain-staking @@ -55,7 +55,10 @@ impl Default for Bond { impl Bond { pub fn from_owner(owner: A) -> Self { - Bond { owner, amount: B::default() } + Bond { + owner, + amount: B::default(), + } } } @@ -79,11 +82,10 @@ impl PartialEq for Bond { } } -#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)] +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] /// The activity status of the collator pub enum CollatorStatus { /// Committed to be online and producing valid blocks (not equivocating) - #[default] Active, /// Temporarily inactive and excused for inactivity Idle, @@ -91,6 +93,12 @@ pub enum CollatorStatus { Leaving(RoundIndex), } +impl Default for CollatorStatus { + fn default() -> CollatorStatus { + CollatorStatus::Active + } +} + #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct BondWithAutoCompound { pub owner: AccountId, @@ -132,8 +140,16 @@ impl PartialEq for CollatorSnapshot { return false; } for ( - BondWithAutoCompound { owner: o1, amount: a1, auto_compound: c1 }, - BondWithAutoCompound { owner: o2, amount: a2, auto_compound: c2 }, + BondWithAutoCompound { + owner: o1, + amount: a1, + auto_compound: c1, + }, + BondWithAutoCompound { + owner: o2, + amount: a2, + auto_compound: c2, + }, ) in self.delegations.iter().zip(other.delegations.iter()) { if o1 != o2 || a1 != a2 || c1 != c2 { @@ -146,11 +162,15 @@ impl PartialEq for CollatorSnapshot { impl Default for CollatorSnapshot { fn default() -> CollatorSnapshot { - CollatorSnapshot { bond: B::default(), delegations: Vec::new(), total: B::default() } + CollatorSnapshot { + bond: B::default(), + delegations: Vec::new(), + total: B::default(), + } } } -#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo)] +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo)] /// Info needed to make delayed payments to stakers after round end pub struct DelayedPayout { /// Total round reward (result of compute_issuance() at round end) @@ -239,7 +259,10 @@ pub struct Delegations { impl Default for Delegations { fn default() -> Delegations { - Delegations { delegations: Vec::new(), total: B::default() } + Delegations { + delegations: Vec::new(), + total: B::default(), + } } } @@ -264,7 +287,10 @@ impl { @@ -278,7 +304,7 @@ impl self.delegations.insert(i, delegation), } } @@ -293,19 +319,26 @@ impl(&self) -> CapacityStatus { match &self.delegations { - x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => - CapacityStatus::Full, + x if x.len() as u32 >= T::MaxBottomDelegationsPerCandidate::get() => { + CapacityStatus::Full + } x if x.is_empty() => CapacityStatus::Empty, _ => CapacityStatus::Partial, } } /// Return last delegation amount without popping the delegation pub fn lowest_delegation_amount(&self) -> Balance { - self.delegations.last().map(|x| x.amount).unwrap_or(Balance::zero()) + self.delegations + .last() + .map(|x| x.amount) + .unwrap_or(Balance::zero()) } /// Return highest delegation amount pub fn highest_delegation_amount(&self) -> Balance { - self.delegations.first().map(|x| x.amount).unwrap_or(Balance::zero()) + self.delegations + .first() + .map(|x| x.amount) + .unwrap_or(Balance::zero()) } } @@ -385,7 +418,10 @@ impl< } pub fn can_leave(&self) -> DispatchResult { if let CollatorStatus::Leaving(when) = self.status { - ensure!(>::get().current >= when, Error::::CandidateCannotLeaveYet); + ensure!( + >::get().current >= when, + Error::::CandidateCannotLeaveYet + ); Ok(()) } else { Err(Error::::CandidateNotLeaving.into()) @@ -433,7 +469,12 @@ impl< if self.bond.is_zero() { T::Currency::remove_lock(COLLATOR_LOCK_ID, &who); } else { - T::Currency::set_lock(COLLATOR_LOCK_ID, &who, self.bond.into(), WithdrawReasons::all()); + T::Currency::set_lock( + COLLATOR_LOCK_ID, + &who, + self.bond.into(), + WithdrawReasons::all(), + ); } self.total_counted = self.total_counted.saturating_sub(amount); let event = Event::CandidateBondedLess { @@ -458,7 +499,10 @@ impl< BalanceOf: Into, { // ensure no pending request - ensure!(self.request.is_none(), Error::::PendingCandidateRequestAlreadyExists); + ensure!( + self.request.is_none(), + Error::::PendingCandidateRequestAlreadyExists + ); // ensure bond above min after decrease ensure!(self.bond > less, Error::::CandidateBondBelowMin); ensure!( @@ -466,7 +510,10 @@ impl< Error::::CandidateBondBelowMin ); let when_executable = >::get().current + T::CandidateBondLessDelay::get(); - self.request = Some(CandidateBondLessRequest { amount: less, when_executable }); + self.request = Some(CandidateBondLessRequest { + amount: less, + when_executable, + }); Ok(when_executable) } /// Execute pending request to decrease the collator self bond @@ -475,7 +522,9 @@ impl< where BalanceOf: From, { - let request = self.request.ok_or(Error::::PendingCandidateRequestsDNE)?; + let request = self + .request + .ok_or(Error::::PendingCandidateRequestsDNE)?; ensure!( request.when_executable <= >::get().current, Error::::PendingCandidateRequestNotDueYet @@ -491,9 +540,11 @@ impl< where BalanceOf: From, { - let request = self.request.ok_or(Error::::PendingCandidateRequestsDNE)?; + let request = self + .request + .ok_or(Error::::PendingCandidateRequestsDNE)?; let event = Event::CancelledCandidateBondLess { - candidate: who, + candidate: who.clone().into(), amount: request.amount.into(), execute_round: request.when_executable, }; @@ -551,7 +602,9 @@ impl< if self.lowest_top_delegation_amount < delegation.amount.into() { // bumps lowest top to the bottom inside this function call less_total_staked = self.add_top_delegation::(candidate, delegation); - DelegatorAdded::AddedToTop { new_total: self.total_counted } + DelegatorAdded::AddedToTop { + new_total: self.total_counted, + } } else { // if bottom is full, only insert if greater than lowest bottom (which will // be bumped out) @@ -567,12 +620,14 @@ impl< self.add_bottom_delegation::(false, candidate, delegation); DelegatorAdded::AddedToBottom } - }, + } // top is either empty or partially full _ => { self.add_top_delegation::(candidate, delegation); - DelegatorAdded::AddedToTop { new_total: self.total_counted } - }, + DelegatorAdded::AddedToTop { + new_total: self.total_counted, + } + } }; Ok((delegator_added, less_total_staked)) } @@ -594,8 +649,9 @@ impl< if top_delegations.delegations.len() as u32 == max_top_delegations_per_candidate { // pop lowest top delegation let new_bottom_delegation = top_delegations.delegations.pop().expect(""); - top_delegations.total = - top_delegations.total.saturating_sub(new_bottom_delegation.amount); + top_delegations.total = top_delegations + .total + .saturating_sub(new_bottom_delegation.amount); if matches!(self.bottom_capacity, CapacityStatus::Full) { less_total_staked = Some(self.lowest_bottom_delegation_amount); } @@ -609,7 +665,7 @@ impl< // only increment delegation count if we are not kicking a bottom delegation self.delegation_count = self.delegation_count.saturating_add(1u32); } - >::insert(candidate, top_delegations); + >::insert(&candidate, top_delegations); less_total_staked } /// Add delegation to bottom delegations @@ -628,8 +684,8 @@ impl< .expect("CandidateInfo existence => BottomDelegations existence"); // if bottom is full, kick the lowest bottom (which is expected to be lower than input // as per check) - let increase_delegation_count = if bottom_delegations.delegations.len() as u32 == - T::MaxBottomDelegationsPerCandidate::get() + let increase_delegation_count = if bottom_delegations.delegations.len() as u32 + == T::MaxBottomDelegationsPerCandidate::get() { let lowest_bottom_to_be_kicked = bottom_delegations .delegations @@ -638,8 +694,9 @@ impl< // EXPECT lowest_bottom_to_be_kicked.amount < delegation.amount enforced by caller // if lowest_bottom_to_be_kicked.amount == delegation.amount, we will still kick // the lowest bottom to enforce first come first served - bottom_delegations.total = - bottom_delegations.total.saturating_sub(lowest_bottom_to_be_kicked.amount); + bottom_delegations.total = bottom_delegations + .total + .saturating_sub(lowest_bottom_to_be_kicked.amount); // update delegator state // total staked is updated via propagation of lowest bottom delegation amount prior // to call @@ -648,12 +705,12 @@ impl< let leaving = delegator_state.delegations.0.len() == 1usize; delegator_state.rm_delegation::(candidate); >::delegation_remove_request_with_state( - candidate, + &candidate, &lowest_bottom_to_be_kicked.owner, &mut delegator_state, ); >::remove_auto_compound( - candidate, + &candidate, &lowest_bottom_to_be_kicked.owner, ); @@ -750,8 +807,9 @@ impl< >::get(candidate).expect("bottom is nonempty as just checked"); // expect already stored greatest to least by bond amount let highest_bottom_delegation = bottom_delegations.delegations.remove(0); - bottom_delegations.total = - bottom_delegations.total.saturating_sub(highest_bottom_delegation.amount); + bottom_delegations.total = bottom_delegations + .total + .saturating_sub(highest_bottom_delegation.amount); self.reset_bottom_data::(&bottom_delegations); >::insert(candidate, bottom_delegations); // insert highest bottom into top delegations @@ -853,7 +911,10 @@ impl< } else { in_top = true; let new_amount = d.amount.saturating_add(more); - Bond { owner: d.owner, amount: new_amount } + Bond { + owner: d.owner, + amount: new_amount, + } } }) .collect(); @@ -909,8 +970,9 @@ impl< .delegations .pop() .expect("Top capacity full => Exists at least 1 top delegation"); - top_delegations.total = - top_delegations.total.saturating_sub(new_bottom_delegation.amount); + top_delegations.total = top_delegations + .total + .saturating_sub(new_bottom_delegation.amount); bottom_delegations.insert_sorted_greatest_to_least(new_bottom_delegation); } // insert into top @@ -930,7 +992,10 @@ impl< d } else { in_bottom = true; - Bond { owner: d.owner, amount: d.amount.saturating_add(more) } + Bond { + owner: d.owner, + amount: d.amount.saturating_add(more), + } } }) .collect(); @@ -991,8 +1056,8 @@ impl< let bond_after_less_than_highest_bottom = bond.saturating_sub(less).into() < self.highest_bottom_delegation_amount; // The top delegations is full and the bottom delegations has at least one delegation - let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full) && - !matches!(self.bottom_capacity, CapacityStatus::Empty); + let full_top_and_nonempty_bottom = matches!(self.top_capacity, CapacityStatus::Full) + && !matches!(self.bottom_capacity, CapacityStatus::Empty); let mut top_delegations = >::get(candidate).ok_or(Error::::CandidateDNE)?; let in_top_after = if bond_after_less_than_highest_bottom && full_top_and_nonempty_bottom { @@ -1020,8 +1085,9 @@ impl< let mut bottom_delegations = >::get(candidate) .expect("CandidateInfo existence => BottomDelegations existence"); let highest_bottom_delegation = bottom_delegations.delegations.remove(0); - bottom_delegations.total = - bottom_delegations.total.saturating_sub(highest_bottom_delegation.amount); + bottom_delegations.total = bottom_delegations + .total + .saturating_sub(highest_bottom_delegation.amount); // insert highest bottom into top top_delegations.insert_sorted_greatest_to_least(highest_bottom_delegation); // insert previous top into bottom @@ -1041,7 +1107,10 @@ impl< d } else { is_in_top = true; - Bond { owner: d.owner, amount: d.amount.saturating_sub(less) } + Bond { + owner: d.owner, + amount: d.amount.saturating_sub(less), + } } }) .collect(); @@ -1076,7 +1145,10 @@ impl< d } else { in_bottom = true; - Bond { owner: d.owner, amount: d.amount.saturating_sub(less) } + Bond { + owner: d.owner, + amount: d.amount.saturating_sub(less), + } } }) .collect(); @@ -1091,12 +1163,12 @@ impl< // Temporary manual implementation for migration testing purposes impl PartialEq for CollatorCandidate { fn eq(&self, other: &Self) -> bool { - let must_be_true = self.id == other.id && - self.bond == other.bond && - self.total_counted == other.total_counted && - self.total_backing == other.total_backing && - self.request == other.request && - self.state == other.state; + let must_be_true = self.id == other.id + && self.bond == other.bond + && self.total_counted == other.total_counted + && self.total_backing == other.total_backing + && self.request == other.request + && self.state == other.state; if !must_be_true { return false; } @@ -1105,15 +1177,37 @@ impl PartialEq for CollatorCandidate { return false; } } - for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in - self.top_delegations.iter().zip(other.top_delegations.iter()) + for ( + Bond { + owner: o1, + amount: a1, + }, + Bond { + owner: o2, + amount: a2, + }, + ) in self + .top_delegations + .iter() + .zip(other.top_delegations.iter()) { if o1 != o2 || a1 != a2 { return false; } } - for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in - self.bottom_delegations.iter().zip(other.bottom_delegations.iter()) + for ( + Bond { + owner: o1, + amount: a1, + }, + Bond { + owner: o2, + amount: a2, + }, + ) in self + .bottom_delegations + .iter() + .zip(other.bottom_delegations.iter()) { if o1 != o2 || a1 != a2 { return false; @@ -1192,15 +1286,23 @@ pub struct Delegator { // Temporary manual implementation for migration testing purposes impl PartialEq for Delegator { fn eq(&self, other: &Self) -> bool { - let must_be_true = self.id == other.id && - self.total == other.total && - self.less_total == other.less_total && - self.status == other.status; + let must_be_true = self.id == other.id + && self.total == other.total + && self.less_total == other.less_total + && self.status == other.status; if !must_be_true { return false; } - for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in - self.delegations.0.iter().zip(other.delegations.0.iter()) + for ( + Bond { + owner: o1, + amount: a1, + }, + Bond { + owner: o2, + amount: a2, + }, + ) in self.delegations.0.iter().zip(other.delegations.0.iter()) { if o1 != o2 || a1 != a2 { return false; @@ -1226,7 +1328,10 @@ impl< pub fn new(id: AccountId, collator: AccountId, amount: Balance) -> Self { Delegator { id, - delegations: OrderedSet::from(vec![Bond { owner: collator, amount }]), + delegations: OrderedSet::from(vec![Bond { + owner: collator, + amount, + }]), total: amount, less_total: Balance::zero(), status: DelegatorStatus::Active, @@ -1319,7 +1424,8 @@ impl< .collect(); if let Some(balance) = amt { self.delegations = OrderedSet::from(delegations); - self.total_sub::(balance).expect("Decreasing lock cannot fail, qed"); + self.total_sub::(balance) + .expect("Decreasing lock cannot fail, qed"); Some(self.total) } else { None @@ -1379,9 +1485,9 @@ impl< /// This will take the current self.total and ensure that a lock of the same amount is applied /// and when increasing the bond lock will also ensure that the account has enough free balance. /// - /// `additional_required_balance` should reflect the change to the amount that should be locked - /// if positive, 0 otherwise (e.g. `min(0, change_in_total_bond)`). This is necessary because it - /// is not possible to query the amount that is locked for a given lock id. + /// `additional_required_balance` should reflect the change to the amount that should be locked if + /// positive, 0 otherwise (e.g. `min(0, change_in_total_bond)`). This is necessary because it is + /// not possible to query the amount that is locked for a given lock id. pub fn adjust_bond_lock( &mut self, additional_required_balance: BondAdjust, @@ -1393,8 +1499,8 @@ impl< match additional_required_balance { BondAdjust::Increase(amount) => { ensure!( - >::get_delegator_stakable_free_balance(&self.id.clone().into()) >= - amount.into(), + >::get_delegator_stakable_balance(&self.id.clone().into()) + >= amount.into(), Error::::InsufficientBalance, ); @@ -1403,7 +1509,7 @@ impl< log::warn!("LOGIC ERROR: request to reserve more than bond total"); return Err(DispatchError::Other("Invalid additional_required_balance")); } - }, + } BondAdjust::Decrease => (), // do nothing on decrease }; @@ -1423,7 +1529,11 @@ impl< /// Retrieves the bond amount that a delegator has provided towards a collator. /// Returns `None` if missing. pub fn get_bond_amount(&self, collator: &AccountId) -> Option { - self.delegations.0.iter().find(|b| &b.owner == collator).map(|b| b.amount) + self.delegations + .0 + .iter() + .find(|b| &b.owner == collator) + .map(|b| b.amount) } } @@ -1521,8 +1631,8 @@ pub mod deprecated { /// [DelegationChange::Revoke] or [DelegationChange::Decrease] action. pub delegations: Vec>, - /// The total counted value locked for the collator, including the self bond + total staked - /// by top delegators. + /// The total counted value locked for the collator, including the self bond + total staked by + /// top delegators. pub total: Balance, } @@ -1532,8 +1642,16 @@ pub mod deprecated { if !must_be_true { return false; } - for (Bond { owner: o1, amount: a1 }, Bond { owner: o2, amount: a2 }) in - self.delegations.iter().zip(other.delegations.iter()) + for ( + Bond { + owner: o1, + amount: a1, + }, + Bond { + owner: o2, + amount: a2, + }, + ) in self.delegations.iter().zip(other.delegations.iter()) { if o1 != o2 || a1 != a2 { return false; @@ -1545,7 +1663,11 @@ pub mod deprecated { impl Default for CollatorSnapshot { fn default() -> CollatorSnapshot { - CollatorSnapshot { bond: B::default(), delegations: Vec::new(), total: B::default() } + CollatorSnapshot { + bond: B::default(), + delegations: Vec::new(), + total: B::default(), + } } } } @@ -1591,22 +1713,30 @@ pub struct RoundInfo { pub first: BlockNumber, /// The length of the current round in number of blocks pub length: u32, + /// The first slot of the current round + pub first_slot: u64, } impl< B: Copy + sp_std::ops::Add + sp_std::ops::Sub + From + PartialOrd, > RoundInfo { - pub fn new(current: RoundIndex, first: B, length: u32) -> RoundInfo { - RoundInfo { current, first, length } + pub fn new(current: RoundIndex, first: B, length: u32, first_slot: u64) -> RoundInfo { + RoundInfo { + current, + first, + length, + first_slot, + } } /// Check if the round should be updated pub fn should_update(&self, now: B) -> bool { now - self.first >= self.length.into() } /// New round - pub fn update(&mut self, now: B) { + pub fn update(&mut self, now: B, now_slot: u64) { self.current = self.current.saturating_add(1u32); self.first = now; + self.first_slot = now_slot; } } impl< @@ -1614,7 +1744,7 @@ impl< > Default for RoundInfo { fn default() -> RoundInfo { - RoundInfo::new(1u32, 1u32.into(), 20u32) + RoundInfo::new(1u32, 1u32.into(), 20u32, 0) } } diff --git a/pallets/parachain-staking/src/weights.rs b/pallets/parachain-staking/src/weights.rs index 53d6bbc9c..89f893113 100644 --- a/pallets/parachain-staking/src/weights.rs +++ b/pallets/parachain-staking/src/weights.rs @@ -1,56 +1,56 @@ -// Copyright 2023-2024 Freeverse.io -// This file is part of LAOS. +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. -// LAOS is free software: you can redistribute it and/or modify +// Moonbeam is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// LAOS is distributed in the hope that it will be useful, +// Moonbeam is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with LAOS. If not, see . +// along with Moonbeam. If not, see . -//! Autogenerated weights for `pallet_parachain_staking` +//! Autogenerated weights for pallet_parachain_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-09-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `MacBook-Pro.local`, CPU: `` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` +//! HOSTNAME: `ip-10-0-0-176`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: -// ./target/release/laos +// ./target/release/moonbeam // benchmark // pallet // --execution=wasm // --wasm-execution=compiled // --pallet // pallet-parachain-staking -// --extrinsic=* +// --extrinsic +// * // --steps // 50 // --repeat // 20 -// --template=.maintain/frame-weight-template.hbs +// --template=./benchmarking/frame-weight-template.hbs // --json-file // raw.json // --output -// ./pallets/parachain-staking/src/weights.rs +// weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] -#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; +use sp_std::marker::PhantomData; -/// Weight functions needed for `pallet_parachain_staking`. +/// Weight functions needed for pallet_parachain_staking. pub trait WeightInfo { fn set_staking_expectations() -> Weight; fn set_inflation() -> Weight; @@ -69,8 +69,8 @@ pub trait WeightInfo { fn candidate_bond_more(x: u32, ) -> Weight; fn schedule_candidate_bond_less() -> Weight; fn execute_candidate_bond_less(x: u32, ) -> Weight; - fn set_candidate_bond_to_zero(x: u32, ) -> Weight; fn cancel_candidate_bond_less() -> Weight; + fn set_candidate_bond_to_zero(x: u32, ) -> Weight; fn delegate(x: u32, y: u32, ) -> Weight; fn schedule_revoke_delegation(x: u32, ) -> Weight; fn delegator_bond_more(x: u32, ) -> Weight; @@ -93,696 +93,653 @@ pub trait WeightInfo { fn notify_inactive_collator() -> Weight; } -/// Weights for `pallet_parachain_staking` using the Substrate node and recommended hardware. +/// Weights for pallet_parachain_staking using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking InflationConfig (r:1 w:1) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) fn set_staking_expectations() -> Weight { // Proof Size summary in bytes: - // Measured: `253` - // Estimated: `1738` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 1738) + // Measured: `88` + // Estimated: `1573` + // Minimum execution time: 15_889_000 picoseconds. + Weight::from_parts(16_404_000, 1573) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking InflationConfig (r:1 w:1) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) fn set_inflation() -> Weight { // Proof Size summary in bytes: - // Measured: `293` - // Estimated: `1778` - // Minimum execution time: 34_000_000 picoseconds. - Weight::from_parts(35_000_000, 1778) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `88` + // Estimated: `1573` + // Minimum execution time: 44_509_000 picoseconds. + Weight::from_parts(45_011_000, 1573) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) - /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking ParachainBondInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking ParachainBondInfo (max_values: Some(1), max_size: None, mode: Measured) fn set_parachain_bond_account() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `1695` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(14_000_000, 1695) + // Measured: `6` + // Estimated: `1491` + // Minimum execution time: 14_675_000 picoseconds. + Weight::from_parts(15_094_000, 1491) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) - /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking ParachainBondInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking ParachainBondInfo (max_values: Some(1), max_size: None, mode: Measured) fn set_parachain_bond_reserve_percent() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `1695` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(14_000_000, 1695) + // Measured: `6` + // Estimated: `1491` + // Minimum execution time: 13_898_000 picoseconds. + Weight::from_parts(14_492_000, 1491) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:1) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking TotalSelected (r:1 w:1) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) fn set_total_selected() -> Weight { // Proof Size summary in bytes: - // Measured: `205` - // Estimated: `1690` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 1690) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `28` + // Estimated: `1513` + // Minimum execution time: 15_666_000 picoseconds. + Weight::from_parts(15_939_000, 1513) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:1) - /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CollatorCommission (r:1 w:1) + /// Proof Skipped: ParachainStaking CollatorCommission (max_values: Some(1), max_size: None, mode: Measured) fn set_collator_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `192` - // Estimated: `1677` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(14_000_000, 1677) + // Measured: `27` + // Estimated: `1512` + // Minimum execution time: 13_997_000 picoseconds. + Weight::from_parts(14_320_000, 1512) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::Round` (r:1 w:1) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking TotalSelected (r:1 w:0) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking InflationConfig (r:1 w:1) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) fn set_blocks_per_round() -> Weight { // Proof Size summary in bytes: - // Measured: `293` - // Estimated: `1778` - // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(39_000_000, 1778) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `116` + // Estimated: `1601` + // Minimum execution time: 48_389_000 picoseconds. + Weight::from_parts(49_554_000, 1601) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:0 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:0 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegatorState (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:0 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:0 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[3, 200]`. fn join_candidates(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1598 + x * (38 ±0)` - // Estimated: `4783 + x * (41 ±0)` - // Minimum execution time: 60_000_000 picoseconds. - Weight::from_parts(64_274_494, 4783) - // Standard Error: 4_265 - .saturating_add(Weight::from_parts(128_563, 0).saturating_mul(x.into())) + // Measured: `1421 + x * (38 ±0)` + // Estimated: `4752 + x * (41 ±0)` + // Minimum execution time: 76_742_000 picoseconds. + Weight::from_parts(88_864_511, 4752) + // Standard Error: 2_004 + .saturating_add(Weight::from_parts(88_538, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) .saturating_add(Weight::from_parts(0, 41).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[3, 200]`. fn schedule_leave_candidates(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `980 + x * (37 ±0)` - // Estimated: `4333 + x * (38 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(27_706_760, 4333) - // Standard Error: 2_826 - .saturating_add(Weight::from_parts(86_272, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `702 + x * (37 ±0)` + // Estimated: `4060 + x * (38 ±0)` + // Minimum execution time: 27_238_000 picoseconds. + Weight::from_parts(34_109_750, 4060) + // Standard Error: 1_116 + .saturating_add(Weight::from_parts(62_292, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:350 w:350) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:350 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:350 w:350) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegatorState (r:349 w:349) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:350 w:350) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:350 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:350 w:350) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[2, 350]`. fn execute_leave_candidates_worst_case(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `751 + x * (431 ±0)` - // Estimated: `5017 + x * (3762 ±0)` - // Minimum execution time: 112_000_000 picoseconds. - Weight::from_parts(116_000_000, 5017) - // Standard Error: 73_097 - .saturating_add(Weight::from_parts(41_339_822, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `1157 + x * (431 ±0)` + // Estimated: `4696 + x * (3762 ±0)` + // Minimum execution time: 141_946_000 picoseconds. + Weight::from_parts(144_961_000, 4696) + // Standard Error: 79_832 + .saturating_add(Weight::from_parts(49_060_154, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:350 w:350) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:350 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:350 w:350) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegatorState (r:349 w:349) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:350 w:350) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:350 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:350 w:350) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[2, 350]`. /// The range of component `y` is `[2, 350]`. fn execute_leave_candidates_ideal(x: u32, _y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `748 + x * (431 ±0)` - // Estimated: `5017 + x * (3762 ±0)` - // Minimum execution time: 105_000_000 picoseconds. - Weight::from_parts(487_068_098, 5017) - // Standard Error: 122_996 - .saturating_add(Weight::from_parts(43_630_003, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `1149 + x * (431 ±0)` + // Estimated: `4696 + x * (3762 ±0)` + // Minimum execution time: 133_121_000 picoseconds. + Weight::from_parts(134_388_000, 4696) + // Standard Error: 34_256 + .saturating_add(Weight::from_parts(50_828_386, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(x.into()))) .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[3, 200]`. fn cancel_leave_candidates(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `908 + x * (37 ±0)` - // Estimated: `4261 + x * (38 ±0)` - // Minimum execution time: 21_000_000 picoseconds. - Weight::from_parts(22_369_911, 4261) - // Standard Error: 3_508 - .saturating_add(Weight::from_parts(112_018, 0).saturating_mul(x.into())) + // Measured: `670 + x * (37 ±0)` + // Estimated: `4028 + x * (38 ±0)` + // Minimum execution time: 25_910_000 picoseconds. + Weight::from_parts(32_465_127, 4028) + // Standard Error: 1_064 + .saturating_add(Weight::from_parts(60_655, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[1, 200]`. fn go_offline(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `805 + x * (38 ±0)` - // Estimated: `4198 + x * (39 ±0)` - // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(24_752_727, 4198) - // Standard Error: 3_215 - .saturating_add(Weight::from_parts(85_135, 0).saturating_mul(x.into())) + // Measured: `567 + x * (38 ±0)` + // Estimated: `3968 + x * (39 ±0)` + // Minimum execution time: 24_471_000 picoseconds. + Weight::from_parts(30_875_133, 3968) + // Standard Error: 1_124 + .saturating_add(Weight::from_parts(66_032, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[1, 200]`. fn go_online(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `769 + x * (38 ±0)` - // Estimated: `4162 + x * (39 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(23_558_629, 4162) - // Standard Error: 3_169 - .saturating_add(Weight::from_parts(90_275, 0).saturating_mul(x.into())) + // Measured: `531 + x * (38 ±0)` + // Estimated: `3932 + x * (39 ±0)` + // Minimum execution time: 24_249_000 picoseconds. + Weight::from_parts(30_765_292, 3932) + // Standard Error: 1_181 + .saturating_add(Weight::from_parts(65_935, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) /// The range of component `x` is `[1, 200]`. fn candidate_bond_more(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1435 + x * (43 ±0)` + // Measured: `1270 + x * (42 ±0)` // Estimated: `4752 + x * (44 ±0)` - // Minimum execution time: 48_000_000 picoseconds. - Weight::from_parts(58_035_521, 4752) - // Standard Error: 4_449 - .saturating_add(Weight::from_parts(120_440, 0).saturating_mul(x.into())) + // Minimum execution time: 68_720_000 picoseconds. + Weight::from_parts(79_722_709, 4752) + // Standard Error: 2_059 + .saturating_add(Weight::from_parts(113_832, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) fn schedule_candidate_bond_less() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `3913` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(21_000_000, 3913) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `171` + // Estimated: `3636` + // Minimum execution time: 21_049_000 picoseconds. + Weight::from_parts(21_735_000, 3636) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) /// The range of component `x` is `[1, 200]`. fn execute_candidate_bond_less(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1658 + x * (42 ±0)` - // Estimated: `4939 + x * (44 ±0)` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(66_258_941, 4939) - // Standard Error: 5_111 - .saturating_add(Weight::from_parts(95_631, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) - } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// The range of component `x` is `[1, 200]`. - fn set_candidate_bond_to_zero(x: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1561 + x * (42 ±0)` - // Estimated: `4842 + x * (44 ±0)` - // Minimum execution time: 48_000_000 picoseconds. - Weight::from_parts(55_481_730, 4842) - // Standard Error: 3_959 - .saturating_add(Weight::from_parts(96_943, 0).saturating_mul(x.into())) + // Measured: `1322 + x * (42 ±0)` + // Estimated: `4752 + x * (43 ±0)` + // Minimum execution time: 71_996_000 picoseconds. + Weight::from_parts(80_620_929, 4752) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(94_580, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) - .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) fn cancel_candidate_bond_less() -> Weight { // Proof Size summary in bytes: - // Measured: `428` - // Estimated: `3893` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(16_000_000, 3893) + // Measured: `191` + // Estimated: `3656` + // Minimum execution time: 18_991_000 picoseconds. + Weight::from_parts(19_491_000, 3656) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_candidate_bond_to_zero(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1322 + x * (42 ±0)` + // Estimated: `4752 + x * (43 ±0)` + // Minimum execution time: 71_996_000 picoseconds. + Weight::from_parts(80_620_929, 4752) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(94_580, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[3, 100]`. /// The range of component `y` is `[2, 300]`. fn delegate(x: u32, y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2724 + x * (79 ±0) + y * (38 ±0)` - // Estimated: `5938 + x * (81 ±0) + y * (39 ±0)` - // Minimum execution time: 83_000_000 picoseconds. - Weight::from_parts(83_018_090, 5938) - // Standard Error: 9_947 - .saturating_add(Weight::from_parts(165_371, 0).saturating_mul(x.into())) - // Standard Error: 3_263 - .saturating_add(Weight::from_parts(80_487, 0).saturating_mul(y.into())) + // Measured: `2479 + x * (79 ±0) + y * (38 ±0)` + // Estimated: `5723 + x * (81 ±0) + y * (39 ±0)` + // Minimum execution time: 120_061_000 picoseconds. + Weight::from_parts(111_894_468, 5723) + // Standard Error: 1_320 + .saturating_add(Weight::from_parts(135_446, 0).saturating_mul(x.into())) + // Standard Error: 433 + .saturating_add(Weight::from_parts(41_110, 0).saturating_mul(y.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) .saturating_add(Weight::from_parts(0, 81).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 39).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn schedule_revoke_delegation(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `953 + x * (42 ±0)` - // Estimated: `4396 + x * (43 ±0)` - // Minimum execution time: 23_000_000 picoseconds. - Weight::from_parts(28_036_968, 4396) - // Standard Error: 1_748 - .saturating_add(Weight::from_parts(100_235, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `566 + x * (42 ±0)` + // Estimated: `4012 + x * (43 ±0)` + // Minimum execution time: 25_479_000 picoseconds. + Weight::from_parts(35_344_986, 4012) + // Standard Error: 803 + .saturating_add(Weight::from_parts(60_212, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn delegator_bond_more(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2163 + x * (79 ±0)` - // Estimated: `5610 + x * (79 ±0)` - // Minimum execution time: 70_000_000 picoseconds. - Weight::from_parts(81_234_901, 5610) - // Standard Error: 3_223 - .saturating_add(Weight::from_parts(159_034, 0).saturating_mul(x.into())) + // Measured: `1996 + x * (79 ±0)` + // Estimated: `5428 + x * (79 ±0)` + // Minimum execution time: 90_985_000 picoseconds. + Weight::from_parts(111_258_553, 5428) + // Standard Error: 1_580 + .saturating_add(Weight::from_parts(109_354, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) .saturating_add(Weight::from_parts(0, 79).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn schedule_delegator_bond_less(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `953 + x * (42 ±0)` - // Estimated: `4396 + x * (43 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(28_726_634, 4396) - // Standard Error: 1_833 - .saturating_add(Weight::from_parts(98_100, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `566 + x * (42 ±0)` + // Estimated: `4012 + x * (43 ±0)` + // Minimum execution time: 25_784_000 picoseconds. + Weight::from_parts(35_792_924, 4012) + // Standard Error: 793 + .saturating_add(Weight::from_parts(60_874, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:0) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:0) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn execute_revoke_delegation() -> Weight { // Proof Size summary in bytes: - // Measured: `1221` + // Measured: `964` // Estimated: `4752` - // Minimum execution time: 93_000_000 picoseconds. - Weight::from_parts(109_000_000, 4752) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Minimum execution time: 113_086_000 picoseconds. + Weight::from_parts(115_421_000, 4752) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn execute_delegator_revoke_delegation_worst() -> Weight { // Proof Size summary in bytes: - // Measured: `37650` - // Estimated: `41115` - // Minimum execution time: 154_000_000 picoseconds. - Weight::from_parts(180_000_000, 41115) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Measured: `37308` + // Estimated: `40773` + // Minimum execution time: 179_325_000 picoseconds. + Weight::from_parts(182_100_000, 40773) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn execute_delegator_bond_less_worst() -> Weight { // Proof Size summary in bytes: - // Measured: `30272` - // Estimated: `33737` - // Minimum execution time: 137_000_000 picoseconds. - Weight::from_parts(158_000_000, 33737) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `29930` + // Estimated: `33395` + // Minimum execution time: 150_818_000 picoseconds. + Weight::from_parts(152_294_000, 33395) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn cancel_delegation_request(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1114 + x * (42 ±0)` - // Estimated: `4543 + x * (43 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(35_122_252, 4543) - // Standard Error: 1_921 - .saturating_add(Weight::from_parts(96_550, 0).saturating_mul(x.into())) + // Measured: `663 + x * (42 ±0)` + // Estimated: `4092 + x * (43 ±0)` + // Minimum execution time: 30_062_000 picoseconds. + Weight::from_parts(37_242_991, 4092) + // Standard Error: 767 + .saturating_add(Weight::from_parts(62_995, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::Points` (r:1 w:0) - /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Staked` (r:1 w:1) - /// Proof: `ParachainStaking::Staked` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:0) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:0) - /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) - /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:2 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:0) - /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelayedPayouts` (r:0 w:1) - /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking Points (r:1 w:0) + /// Proof Skipped: ParachainStaking Points (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Staked (r:1 w:1) + /// Proof Skipped: ParachainStaking Staked (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking InflationConfig (r:1 w:0) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking ParachainBondInfo (r:1 w:0) + /// Proof Skipped: ParachainStaking ParachainBondInfo (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking CollatorCommission (r:1 w:0) + /// Proof Skipped: ParachainStaking CollatorCommission (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking DelayedPayouts (r:0 w:1) + /// Proof Skipped: ParachainStaking DelayedPayouts (max_values: None, max_size: None, mode: Measured) fn prepare_staking_payouts() -> Weight { // Proof Size summary in bytes: - // Measured: `594` - // Estimated: `6172` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(29_000_000, 6172) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `380` + // Estimated: `3845` + // Minimum execution time: 48_260_000 picoseconds. + Weight::from_parts(49_856_000, 3845) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:0) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:0) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `y` is `[0, 100]`. fn get_rewardable_delegators(y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `349 + y * (36 ±0)` - // Estimated: `3814 + y * (36 ±0)` - // Minimum execution time: 8_000_000 picoseconds. - Weight::from_parts(9_578_354, 3814) - // Standard Error: 1_539 - .saturating_add(Weight::from_parts(36_261, 0).saturating_mul(y.into())) + // Measured: `73 + y * (36 ±0)` + // Estimated: `3537 + y * (36 ±0)` + // Minimum execution time: 8_183_000 picoseconds. + Weight::from_parts(10_416_160, 3537) + // Standard Error: 780 + .saturating_add(Weight::from_parts(44_865, 0).saturating_mul(y.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:0) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:50 w:0) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:50 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:50 w:0) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:50 w:0) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::SelectedCandidates` (r:0 w:1) - /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AtStake` (r:0 w:50) - /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking TotalSelected (r:1 w:0) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:0) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:51 w:0) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:51 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:51 w:0) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:51 w:0) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking SelectedCandidates (r:0 w:1) + /// Proof Skipped: ParachainStaking SelectedCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking AtStake (r:0 w:51) + /// Proof Skipped: ParachainStaking AtStake (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 50]`. /// The range of component `y` is `[0, 100]`. fn select_top_candidates(x: u32, y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + x * (3817 ±0) + y * (1800 ±0)` - // Estimated: `7646 + x * (3860 ±41) + y * (639 ±20)` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(11_000_000, 7646) - // Standard Error: 71_042 - .saturating_add(Weight::from_parts(18_570_869, 0).saturating_mul(x.into())) - // Standard Error: 35_426 - .saturating_add(Weight::from_parts(1_053_572, 0).saturating_mul(y.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `0 + x * (3816 ±0) + y * (1800 ±0)` + // Estimated: `3730 + x * (3975 ±39) + y * (639 ±19)` + // Minimum execution time: 31_870_000 picoseconds. + Weight::from_parts(32_158_000, 3730) + // Standard Error: 51_554 + .saturating_add(Weight::from_parts(22_540_635, 0).saturating_mul(x.into())) + // Standard Error: 25_709 + .saturating_add(Weight::from_parts(957_745, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(x.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) - .saturating_add(Weight::from_parts(0, 3860).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 3975).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 639).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) - /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:350 w:350) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:349 w:349) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:349 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: System Account (r:349 w:349) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:349 w:349) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:349 w:349) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:349 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. /// The range of component `y` is `[0, 349]`. /// The range of component `z` is `[0, 349]`. fn pay_one_collator_reward_best(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + x * (395 ±0) + y * (156 ±0) + z * (41 ±0)` - // Estimated: `125392 + x * (2591 ±20) + y * (2234 ±20) + z * (28 ±0)` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 125392) - // Standard Error: 650_921 - .saturating_add(Weight::from_parts(91_561_977, 0).saturating_mul(x.into())) - // Standard Error: 650_921 - .saturating_add(Weight::from_parts(38_016_863, 0).saturating_mul(y.into())) + // Estimated: `125757 + x * (2591 ±19) + y * (2234 ±19) + z * (28 ±0)` + // Minimum execution time: 459_000 picoseconds. + Weight::from_parts(472_000, 125757) + // Standard Error: 685_607 + .saturating_add(Weight::from_parts(74_007_162, 0).saturating_mul(x.into())) + // Standard Error: 685_607 + .saturating_add(Weight::from_parts(43_825_857, 0).saturating_mul(y.into())) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(y.into()))) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(x.into()))) @@ -791,145 +748,141 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(0, 2234).saturating_mul(y.into())) .saturating_add(Weight::from_parts(0, 28).saturating_mul(z.into())) } - /// Storage: `ParachainStaking::DelayedPayouts` (r:1 w:0) - /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Points` (r:1 w:0) - /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AtStake` (r:2 w:1) - /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AwardedPts` (r:1 w:1) - /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:302 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) - /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelayedPayouts (r:1 w:0) + /// Proof Skipped: ParachainStaking DelayedPayouts (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Points (r:1 w:0) + /// Proof Skipped: ParachainStaking Points (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AtStake (r:2 w:1) + /// Proof Skipped: ParachainStaking AtStake (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AwardedPts (r:1 w:1) + /// Proof Skipped: ParachainStaking AwardedPts (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: MoonbeamOrbiters OrbiterPerRound (r:1 w:0) + /// Proof Skipped: MoonbeamOrbiters OrbiterPerRound (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:301 w:301) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) /// The range of component `y` is `[0, 300]`. fn pay_one_collator_reward(y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `795 + y * (109 ±0)` - // Estimated: `6755 + y * (2591 ±0)` - // Minimum execution time: 39_000_000 picoseconds. - Weight::from_parts(67_055_208, 6755) - // Standard Error: 21_113 - .saturating_add(Weight::from_parts(6_363_314, 0).saturating_mul(y.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1208 + y * (160 ±0)` + // Estimated: `6978 + y * (2591 ±0)` + // Minimum execution time: 65_428_000 picoseconds. + Weight::from_parts(68_535_135, 6978) + // Standard Error: 6_093 + .saturating_add(Weight::from_parts(25_186_464, 0).saturating_mul(y.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(y.into()))) .saturating_add(Weight::from_parts(0, 2591).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn base_on_initialize() -> Weight { // Proof Size summary in bytes: - // Measured: `347` - // Estimated: `1832` - // Minimum execution time: 6_000_000 picoseconds. - Weight::from_parts(6_000_000, 1832) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_024_000 picoseconds. + Weight::from_parts(2_168_000, 0) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 300]`. /// The range of component `y` is `[0, 100]`. fn set_auto_compound(x: u32, y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1022 + x * (22 ±0) + y * (36 ±0)` - // Estimated: `4378 + x * (23 ±0) + y * (36 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(29_825_160, 4378) - // Standard Error: 1_769 - .saturating_add(Weight::from_parts(49_729, 0).saturating_mul(x.into())) - // Standard Error: 5_296 - .saturating_add(Weight::from_parts(47_869, 0).saturating_mul(y.into())) + // Measured: `671 + x * (22 ±0) + y * (36 ±0)` + // Estimated: `4027 + x * (23 ±0) + y * (36 ±0)` + // Minimum execution time: 34_494_000 picoseconds. + Weight::from_parts(33_677_881, 4027) + // Standard Error: 278 + .saturating_add(Weight::from_parts(45_734, 0).saturating_mul(x.into())) + // Standard Error: 833 + .saturating_add(Weight::from_parts(39_753, 0).saturating_mul(y.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 23).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[0, 350]`. /// The range of component `y` is `[0, 349]`. /// The range of component `z` is `[0, 99]`. fn delegate_with_auto_compound(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + x * (60 ±0) + y * (21 ±0) + z * (78 ±0)` - // Estimated: `26419 + x * (44 ±0) + y * (19 ±0) + z * (76 ±1)` - // Minimum execution time: 90_000_000 picoseconds. - Weight::from_parts(90_069_327, 26419) - // Standard Error: 2_700 - .saturating_add(Weight::from_parts(128_216, 0).saturating_mul(x.into())) - // Standard Error: 2_707 - .saturating_add(Weight::from_parts(7_494, 0).saturating_mul(y.into())) - // Standard Error: 9_535 - .saturating_add(Weight::from_parts(138_309, 0).saturating_mul(z.into())) + // Estimated: `26253 + x * (44 ±0) + y * (19 ±0) + z * (76 ±1)` + // Minimum execution time: 130_103_000 picoseconds. + Weight::from_parts(116_616_415, 26253) + // Standard Error: 1_141 + .saturating_add(Weight::from_parts(117_041, 0).saturating_mul(x.into())) + // Standard Error: 4_030 + .saturating_add(Weight::from_parts(178_483, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 19).saturating_mul(y.into())) .saturating_add(Weight::from_parts(0, 76).saturating_mul(z.into())) } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:2 w:2) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:2 w:2) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:2 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:2 w:2) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:2 w:2) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:2 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn delegate_with_auto_compound_worst() -> Weight { // Proof Size summary in bytes: - // Measured: `48446` - // Estimated: `54386` - // Minimum execution time: 213_000_000 picoseconds. - Weight::from_parts(242_000_000, 54386) + // Measured: `48167` + // Estimated: `54107` + // Minimum execution time: 276_584_000 picoseconds. + Weight::from_parts(279_594_000, 54107) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) fn mint_collator_reward() -> Weight { // Proof Size summary in bytes: - // Measured: `91` + // Measured: `128` // Estimated: `3581` - // Minimum execution time: 22_000_000 picoseconds. - Weight::from_parts(23_000_000, 3581) + // Minimum execution time: 28_741_000 picoseconds. + Weight::from_parts(29_344_000, 3581) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -946,722 +899,680 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `ParachainStaking::EnableMarkingOffline` (r:1 w:0) - /// Proof: `ParachainStaking::EnableMarkingOffline` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::SelectedCandidates` (r:1 w:0) - /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AtStake` (r:1 w:0) - /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AwardedPts` (r:1 w:0) - /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking EnableMarkingOffline (r:1 w:0) + /// Proof Skipped: ParachainStaking EnableMarkingOffline (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking TotalSelected (r:1 w:0) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking SelectedCandidates (r:1 w:0) + /// Proof Skipped: ParachainStaking SelectedCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking AtStake (r:2 w:0) + /// Proof Skipped: ParachainStaking AtStake (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AwardedPts (r:2 w:0) + /// Proof Skipped: ParachainStaking AwardedPts (max_values: None, max_size: None, mode: Measured) + /// Storage: MoonbeamOrbiters OrbiterPerRound (r:1 w:0) + /// Proof Skipped: MoonbeamOrbiters OrbiterPerRound (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) fn notify_inactive_collator() -> Weight { // Proof Size summary in bytes: - // Measured: `11709` - // Estimated: `15174` - // Minimum execution time: 59_000_000 picoseconds. - Weight::from_parts(74_000_000, 15174) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `11494` + // Estimated: `17434` + // Minimum execution time: 41_130_000 picoseconds. + Weight::from_parts(41_130_000, 0) + .saturating_add(Weight::from_parts(0, 17434)) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests. +// For backwards compatibility and tests impl WeightInfo for () { - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking InflationConfig (r:1 w:1) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) fn set_staking_expectations() -> Weight { // Proof Size summary in bytes: - // Measured: `253` - // Estimated: `1738` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 1738) + // Measured: `88` + // Estimated: `1573` + // Minimum execution time: 15_889_000 picoseconds. + Weight::from_parts(16_404_000, 1573) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking InflationConfig (r:1 w:1) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) fn set_inflation() -> Weight { // Proof Size summary in bytes: - // Measured: `293` - // Estimated: `1778` - // Minimum execution time: 34_000_000 picoseconds. - Weight::from_parts(35_000_000, 1778) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `88` + // Estimated: `1573` + // Minimum execution time: 44_509_000 picoseconds. + Weight::from_parts(45_011_000, 1573) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) - /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking ParachainBondInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking ParachainBondInfo (max_values: Some(1), max_size: None, mode: Measured) fn set_parachain_bond_account() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `1695` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(14_000_000, 1695) + // Measured: `6` + // Estimated: `1491` + // Minimum execution time: 14_675_000 picoseconds. + Weight::from_parts(15_094_000, 1491) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:1) - /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking ParachainBondInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking ParachainBondInfo (max_values: Some(1), max_size: None, mode: Measured) fn set_parachain_bond_reserve_percent() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `1695` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(14_000_000, 1695) + // Measured: `6` + // Estimated: `1491` + // Minimum execution time: 13_898_000 picoseconds. + Weight::from_parts(14_492_000, 1491) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:1) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking TotalSelected (r:1 w:1) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) fn set_total_selected() -> Weight { // Proof Size summary in bytes: - // Measured: `205` - // Estimated: `1690` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 1690) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `28` + // Estimated: `1513` + // Minimum execution time: 15_666_000 picoseconds. + Weight::from_parts(15_939_000, 1513) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:1) - /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CollatorCommission (r:1 w:1) + /// Proof Skipped: ParachainStaking CollatorCommission (max_values: Some(1), max_size: None, mode: Measured) fn set_collator_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `192` - // Estimated: `1677` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(14_000_000, 1677) + // Measured: `27` + // Estimated: `1512` + // Minimum execution time: 13_997_000 picoseconds. + Weight::from_parts(14_320_000, 1512) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::Round` (r:1 w:1) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:1) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking TotalSelected (r:1 w:0) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking InflationConfig (r:1 w:1) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) fn set_blocks_per_round() -> Weight { // Proof Size summary in bytes: - // Measured: `293` - // Estimated: `1778` - // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(39_000_000, 1778) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `116` + // Estimated: `1601` + // Minimum execution time: 48_389_000 picoseconds. + Weight::from_parts(49_554_000, 1601) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:0 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:0 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegatorState (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:0 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:0 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[3, 200]`. fn join_candidates(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1598 + x * (38 ±0)` - // Estimated: `4783 + x * (41 ±0)` - // Minimum execution time: 60_000_000 picoseconds. - Weight::from_parts(64_274_494, 4783) - // Standard Error: 4_265 - .saturating_add(Weight::from_parts(128_563, 0).saturating_mul(x.into())) + // Measured: `1421 + x * (38 ±0)` + // Estimated: `4752 + x * (41 ±0)` + // Minimum execution time: 76_742_000 picoseconds. + Weight::from_parts(88_864_511, 4752) + // Standard Error: 2_004 + .saturating_add(Weight::from_parts(88_538, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) .saturating_add(Weight::from_parts(0, 41).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[3, 200]`. fn schedule_leave_candidates(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `980 + x * (37 ±0)` - // Estimated: `4333 + x * (38 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(27_706_760, 4333) - // Standard Error: 2_826 - .saturating_add(Weight::from_parts(86_272, 0).saturating_mul(x.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `702 + x * (37 ±0)` + // Estimated: `4060 + x * (38 ±0)` + // Minimum execution time: 27_238_000 picoseconds. + Weight::from_parts(34_109_750, 4060) + // Standard Error: 1_116 + .saturating_add(Weight::from_parts(62_292, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:350 w:350) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:350 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:350 w:350) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegatorState (r:349 w:349) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:350 w:350) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:350 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:350 w:350) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[2, 350]`. fn execute_leave_candidates_worst_case(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `751 + x * (431 ±0)` - // Estimated: `5017 + x * (3762 ±0)` - // Minimum execution time: 112_000_000 picoseconds. - Weight::from_parts(116_000_000, 5017) - // Standard Error: 73_097 - .saturating_add(Weight::from_parts(41_339_822, 0).saturating_mul(x.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `1157 + x * (431 ±0)` + // Estimated: `4696 + x * (3762 ±0)` + // Minimum execution time: 141_946_000 picoseconds. + Weight::from_parts(144_961_000, 4696) + // Standard Error: 79_832 + .saturating_add(Weight::from_parts(49_060_154, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(x.into()))) .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:350 w:350) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:350 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:350 w:350) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegatorState (r:349 w:349) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:350 w:350) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:350 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:350 w:350) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[2, 350]`. /// The range of component `y` is `[2, 350]`. fn execute_leave_candidates_ideal(x: u32, _y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `748 + x * (431 ±0)` - // Estimated: `5017 + x * (3762 ±0)` - // Minimum execution time: 105_000_000 picoseconds. - Weight::from_parts(487_068_098, 5017) - // Standard Error: 122_996 - .saturating_add(Weight::from_parts(43_630_003, 0).saturating_mul(x.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `1149 + x * (431 ±0)` + // Estimated: `4696 + x * (3762 ±0)` + // Minimum execution time: 133_121_000 picoseconds. + Weight::from_parts(134_388_000, 4696) + // Standard Error: 34_256 + .saturating_add(Weight::from_parts(50_828_386, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(x.into()))) .saturating_add(Weight::from_parts(0, 3762).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[3, 200]`. fn cancel_leave_candidates(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `908 + x * (37 ±0)` - // Estimated: `4261 + x * (38 ±0)` - // Minimum execution time: 21_000_000 picoseconds. - Weight::from_parts(22_369_911, 4261) - // Standard Error: 3_508 - .saturating_add(Weight::from_parts(112_018, 0).saturating_mul(x.into())) + // Measured: `670 + x * (37 ±0)` + // Estimated: `4028 + x * (38 ±0)` + // Minimum execution time: 25_910_000 picoseconds. + Weight::from_parts(32_465_127, 4028) + // Standard Error: 1_064 + .saturating_add(Weight::from_parts(60_655, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 38).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[1, 200]`. fn go_offline(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `805 + x * (38 ±0)` - // Estimated: `4198 + x * (39 ±0)` - // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(24_752_727, 4198) - // Standard Error: 3_215 - .saturating_add(Weight::from_parts(85_135, 0).saturating_mul(x.into())) + // Measured: `567 + x * (38 ±0)` + // Estimated: `3968 + x * (39 ±0)` + // Minimum execution time: 24_471_000 picoseconds. + Weight::from_parts(30_875_133, 3968) + // Standard Error: 1_124 + .saturating_add(Weight::from_parts(66_032, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[1, 200]`. fn go_online(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `769 + x * (38 ±0)` - // Estimated: `4162 + x * (39 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(23_558_629, 4162) - // Standard Error: 3_169 - .saturating_add(Weight::from_parts(90_275, 0).saturating_mul(x.into())) + // Measured: `531 + x * (38 ±0)` + // Estimated: `3932 + x * (39 ±0)` + // Minimum execution time: 24_249_000 picoseconds. + Weight::from_parts(30_765_292, 3932) + // Standard Error: 1_181 + .saturating_add(Weight::from_parts(65_935, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 39).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) /// The range of component `x` is `[1, 200]`. fn candidate_bond_more(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1435 + x * (43 ±0)` + // Measured: `1270 + x * (42 ±0)` // Estimated: `4752 + x * (44 ±0)` - // Minimum execution time: 48_000_000 picoseconds. - Weight::from_parts(58_035_521, 4752) - // Standard Error: 4_449 - .saturating_add(Weight::from_parts(120_440, 0).saturating_mul(x.into())) + // Minimum execution time: 68_720_000 picoseconds. + Weight::from_parts(79_722_709, 4752) + // Standard Error: 2_059 + .saturating_add(Weight::from_parts(113_832, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) fn schedule_candidate_bond_less() -> Weight { // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `3913` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(21_000_000, 3913) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `171` + // Estimated: `3636` + // Minimum execution time: 21_049_000 picoseconds. + Weight::from_parts(21_735_000, 3636) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) /// The range of component `x` is `[1, 200]`. fn execute_candidate_bond_less(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1658 + x * (42 ±0)` - // Estimated: `4939 + x * (44 ±0)` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(66_258_941, 4939) - // Standard Error: 5_111 - .saturating_add(Weight::from_parts(95_631, 0).saturating_mul(x.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) - } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// The range of component `x` is `[1, 200]`. - fn set_candidate_bond_to_zero(x: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1561 + x * (42 ±0)` - // Estimated: `4842 + x * (44 ±0)` - // Minimum execution time: 48_000_000 picoseconds. - Weight::from_parts(55_481_730, 4842) - // Standard Error: 3_959 - .saturating_add(Weight::from_parts(96_943, 0).saturating_mul(x.into())) + // Measured: `1322 + x * (42 ±0)` + // Estimated: `4752 + x * (43 ±0)` + // Minimum execution time: 71_996_000 picoseconds. + Weight::from_parts(80_620_929, 4752) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(94_580, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) - .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) fn cancel_candidate_bond_less() -> Weight { // Proof Size summary in bytes: - // Measured: `428` - // Estimated: `3893` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(16_000_000, 3893) + // Measured: `191` + // Estimated: `3656` + // Minimum execution time: 18_991_000 picoseconds. + Weight::from_parts(19_491_000, 3656) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_candidate_bond_to_zero(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1322 + x * (42 ±0)` + // Estimated: `4752 + x * (43 ±0)` + // Minimum execution time: 71_996_000 picoseconds. + Weight::from_parts(80_620_929, 4752) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(94_580, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) + } + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[3, 100]`. /// The range of component `y` is `[2, 300]`. fn delegate(x: u32, y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2724 + x * (79 ±0) + y * (38 ±0)` - // Estimated: `5938 + x * (81 ±0) + y * (39 ±0)` - // Minimum execution time: 83_000_000 picoseconds. - Weight::from_parts(83_018_090, 5938) - // Standard Error: 9_947 - .saturating_add(Weight::from_parts(165_371, 0).saturating_mul(x.into())) - // Standard Error: 3_263 - .saturating_add(Weight::from_parts(80_487, 0).saturating_mul(y.into())) + // Measured: `2479 + x * (79 ±0) + y * (38 ±0)` + // Estimated: `5723 + x * (81 ±0) + y * (39 ±0)` + // Minimum execution time: 120_061_000 picoseconds. + Weight::from_parts(111_894_468, 5723) + // Standard Error: 1_320 + .saturating_add(Weight::from_parts(135_446, 0).saturating_mul(x.into())) + // Standard Error: 433 + .saturating_add(Weight::from_parts(41_110, 0).saturating_mul(y.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) .saturating_add(Weight::from_parts(0, 81).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 39).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn schedule_revoke_delegation(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `953 + x * (42 ±0)` - // Estimated: `4396 + x * (43 ±0)` - // Minimum execution time: 23_000_000 picoseconds. - Weight::from_parts(28_036_968, 4396) - // Standard Error: 1_748 - .saturating_add(Weight::from_parts(100_235, 0).saturating_mul(x.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `566 + x * (42 ±0)` + // Estimated: `4012 + x * (43 ±0)` + // Minimum execution time: 25_479_000 picoseconds. + Weight::from_parts(35_344_986, 4012) + // Standard Error: 803 + .saturating_add(Weight::from_parts(60_212, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn delegator_bond_more(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2163 + x * (79 ±0)` - // Estimated: `5610 + x * (79 ±0)` - // Minimum execution time: 70_000_000 picoseconds. - Weight::from_parts(81_234_901, 5610) - // Standard Error: 3_223 - .saturating_add(Weight::from_parts(159_034, 0).saturating_mul(x.into())) + // Measured: `1996 + x * (79 ±0)` + // Estimated: `5428 + x * (79 ±0)` + // Minimum execution time: 90_985_000 picoseconds. + Weight::from_parts(111_258_553, 5428) + // Standard Error: 1_580 + .saturating_add(Weight::from_parts(109_354, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) .saturating_add(Weight::from_parts(0, 79).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn schedule_delegator_bond_less(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `953 + x * (42 ±0)` - // Estimated: `4396 + x * (43 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(28_726_634, 4396) - // Standard Error: 1_833 - .saturating_add(Weight::from_parts(98_100, 0).saturating_mul(x.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `566 + x * (42 ±0)` + // Estimated: `4012 + x * (43 ±0)` + // Minimum execution time: 25_784_000 picoseconds. + Weight::from_parts(35_792_924, 4012) + // Standard Error: 793 + .saturating_add(Weight::from_parts(60_874, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:0) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:0) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn execute_revoke_delegation() -> Weight { // Proof Size summary in bytes: - // Measured: `1221` + // Measured: `964` // Estimated: `4752` - // Minimum execution time: 93_000_000 picoseconds. - Weight::from_parts(109_000_000, 4752) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Minimum execution time: 113_086_000 picoseconds. + Weight::from_parts(115_421_000, 4752) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn execute_delegator_revoke_delegation_worst() -> Weight { // Proof Size summary in bytes: - // Measured: `37650` - // Estimated: `41115` - // Minimum execution time: 154_000_000 picoseconds. - Weight::from_parts(180_000_000, 41115) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Measured: `37308` + // Estimated: `40773` + // Minimum execution time: 179_325_000 picoseconds. + Weight::from_parts(182_100_000, 40773) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn execute_delegator_bond_less_worst() -> Weight { // Proof Size summary in bytes: - // Measured: `30272` - // Estimated: `33737` - // Minimum execution time: 137_000_000 picoseconds. - Weight::from_parts(158_000_000, 33737) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `29930` + // Estimated: `33395` + // Minimum execution time: 150_818_000 picoseconds. + Weight::from_parts(152_294_000, 33395) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. fn cancel_delegation_request(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1114 + x * (42 ±0)` - // Estimated: `4543 + x * (43 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(35_122_252, 4543) - // Standard Error: 1_921 - .saturating_add(Weight::from_parts(96_550, 0).saturating_mul(x.into())) + // Measured: `663 + x * (42 ±0)` + // Estimated: `4092 + x * (43 ±0)` + // Minimum execution time: 30_062_000 picoseconds. + Weight::from_parts(37_242_991, 4092) + // Standard Error: 767 + .saturating_add(Weight::from_parts(62_995, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 43).saturating_mul(x.into())) } - /// Storage: `ParachainStaking::Points` (r:1 w:0) - /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Staked` (r:1 w:1) - /// Proof: `ParachainStaking::Staked` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::InflationConfig` (r:1 w:0) - /// Proof: `ParachainStaking::InflationConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::ParachainBondInfo` (r:1 w:0) - /// Proof: `ParachainStaking::ParachainBondInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) - /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:2 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CollatorCommission` (r:1 w:0) - /// Proof: `ParachainStaking::CollatorCommission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelayedPayouts` (r:0 w:1) - /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking Points (r:1 w:0) + /// Proof Skipped: ParachainStaking Points (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Staked (r:1 w:1) + /// Proof Skipped: ParachainStaking Staked (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking InflationConfig (r:1 w:0) + /// Proof Skipped: ParachainStaking InflationConfig (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking ParachainBondInfo (r:1 w:0) + /// Proof Skipped: ParachainStaking ParachainBondInfo (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking CollatorCommission (r:1 w:0) + /// Proof Skipped: ParachainStaking CollatorCommission (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking DelayedPayouts (r:0 w:1) + /// Proof Skipped: ParachainStaking DelayedPayouts (max_values: None, max_size: None, mode: Measured) fn prepare_staking_payouts() -> Weight { // Proof Size summary in bytes: - // Measured: `594` - // Estimated: `6172` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(29_000_000, 6172) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `380` + // Estimated: `3845` + // Minimum execution time: 48_260_000 picoseconds. + Weight::from_parts(49_856_000, 3845) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:0) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:0) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `y` is `[0, 100]`. fn get_rewardable_delegators(y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `349 + y * (36 ±0)` - // Estimated: `3814 + y * (36 ±0)` - // Minimum execution time: 8_000_000 picoseconds. - Weight::from_parts(9_578_354, 3814) - // Standard Error: 1_539 - .saturating_add(Weight::from_parts(36_261, 0).saturating_mul(y.into())) + // Measured: `73 + y * (36 ±0)` + // Estimated: `3537 + y * (36 ±0)` + // Minimum execution time: 8_183_000 picoseconds. + Weight::from_parts(10_416_160, 3537) + // Standard Error: 780 + .saturating_add(Weight::from_parts(44_865, 0).saturating_mul(y.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:0) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:50 w:0) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:50 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:50 w:0) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:50 w:0) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::SelectedCandidates` (r:0 w:1) - /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AtStake` (r:0 w:50) - /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking TotalSelected (r:1 w:0) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:0) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:51 w:0) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:51 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:51 w:0) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:51 w:0) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking SelectedCandidates (r:0 w:1) + /// Proof Skipped: ParachainStaking SelectedCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking AtStake (r:0 w:51) + /// Proof Skipped: ParachainStaking AtStake (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 50]`. /// The range of component `y` is `[0, 100]`. fn select_top_candidates(x: u32, y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + x * (3817 ±0) + y * (1800 ±0)` - // Estimated: `7646 + x * (3860 ±41) + y * (639 ±20)` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(11_000_000, 7646) - // Standard Error: 71_042 - .saturating_add(Weight::from_parts(18_570_869, 0).saturating_mul(x.into())) - // Standard Error: 35_426 - .saturating_add(Weight::from_parts(1_053_572, 0).saturating_mul(y.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `0 + x * (3816 ±0) + y * (1800 ±0)` + // Estimated: `3730 + x * (3975 ±39) + y * (639 ±19)` + // Minimum execution time: 31_870_000 picoseconds. + Weight::from_parts(32_158_000, 3730) + // Standard Error: 51_554 + .saturating_add(Weight::from_parts(22_540_635, 0).saturating_mul(x.into())) + // Standard Error: 25_709 + .saturating_add(Weight::from_parts(957_745, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(x.into()))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(x.into()))) - .saturating_add(Weight::from_parts(0, 3860).saturating_mul(x.into())) + .saturating_add(Weight::from_parts(0, 3975).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 639).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) - /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:350 w:350) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:349 w:349) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:349 w:349) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:349 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: System Account (r:349 w:349) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:349 w:349) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:349 w:349) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:349 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 349]`. /// The range of component `y` is `[0, 349]`. /// The range of component `z` is `[0, 349]`. fn pay_one_collator_reward_best(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + x * (395 ±0) + y * (156 ±0) + z * (41 ±0)` - // Estimated: `125392 + x * (2591 ±20) + y * (2234 ±20) + z * (28 ±0)` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 125392) - // Standard Error: 650_921 - .saturating_add(Weight::from_parts(91_561_977, 0).saturating_mul(x.into())) - // Standard Error: 650_921 - .saturating_add(Weight::from_parts(38_016_863, 0).saturating_mul(y.into())) + // Estimated: `125757 + x * (2591 ±19) + y * (2234 ±19) + z * (28 ±0)` + // Minimum execution time: 459_000 picoseconds. + Weight::from_parts(472_000, 125757) + // Standard Error: 685_607 + .saturating_add(Weight::from_parts(74_007_162, 0).saturating_mul(x.into())) + // Standard Error: 685_607 + .saturating_add(Weight::from_parts(43_825_857, 0).saturating_mul(y.into())) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(y.into()))) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(x.into()))) @@ -1670,145 +1581,141 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(0, 2234).saturating_mul(y.into())) .saturating_add(Weight::from_parts(0, 28).saturating_mul(z.into())) } - /// Storage: `ParachainStaking::DelayedPayouts` (r:1 w:0) - /// Proof: `ParachainStaking::DelayedPayouts` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Points` (r:1 w:0) - /// Proof: `ParachainStaking::Points` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AtStake` (r:2 w:1) - /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AwardedPts` (r:1 w:1) - /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:0) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:302 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::RewardsAccount` (r:1 w:0) - /// Proof: `ParachainStaking::RewardsAccount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelayedPayouts (r:1 w:0) + /// Proof Skipped: ParachainStaking DelayedPayouts (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking Points (r:1 w:0) + /// Proof Skipped: ParachainStaking Points (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AtStake (r:2 w:1) + /// Proof Skipped: ParachainStaking AtStake (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AwardedPts (r:1 w:1) + /// Proof Skipped: ParachainStaking AwardedPts (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: MoonbeamOrbiters OrbiterPerRound (r:1 w:0) + /// Proof Skipped: MoonbeamOrbiters OrbiterPerRound (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:301 w:301) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) /// The range of component `y` is `[0, 300]`. fn pay_one_collator_reward(y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `795 + y * (109 ±0)` - // Estimated: `6755 + y * (2591 ±0)` - // Minimum execution time: 39_000_000 picoseconds. - Weight::from_parts(67_055_208, 6755) - // Standard Error: 21_113 - .saturating_add(Weight::from_parts(6_363_314, 0).saturating_mul(y.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1208 + y * (160 ±0)` + // Estimated: `6978 + y * (2591 ±0)` + // Minimum execution time: 65_428_000 picoseconds. + Weight::from_parts(68_535_135, 6978) + // Standard Error: 6_093 + .saturating_add(Weight::from_parts(25_186_464, 0).saturating_mul(y.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(y.into()))) .saturating_add(Weight::from_parts(0, 2591).saturating_mul(y.into())) } - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn base_on_initialize() -> Weight { // Proof Size summary in bytes: - // Measured: `347` - // Estimated: `1832` - // Minimum execution time: 6_000_000 picoseconds. - Weight::from_parts(6_000_000, 1832) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_024_000 picoseconds. + Weight::from_parts(2_168_000, 0) } - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:0) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking DelegatorState (r:1 w:0) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) /// The range of component `x` is `[0, 300]`. /// The range of component `y` is `[0, 100]`. fn set_auto_compound(x: u32, y: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1022 + x * (22 ±0) + y * (36 ±0)` - // Estimated: `4378 + x * (23 ±0) + y * (36 ±0)` - // Minimum execution time: 24_000_000 picoseconds. - Weight::from_parts(29_825_160, 4378) - // Standard Error: 1_769 - .saturating_add(Weight::from_parts(49_729, 0).saturating_mul(x.into())) - // Standard Error: 5_296 - .saturating_add(Weight::from_parts(47_869, 0).saturating_mul(y.into())) + // Measured: `671 + x * (22 ±0) + y * (36 ±0)` + // Estimated: `4027 + x * (23 ±0) + y * (36 ±0)` + // Minimum execution time: 34_494_000 picoseconds. + Weight::from_parts(33_677_881, 4027) + // Standard Error: 278 + .saturating_add(Weight::from_parts(45_734, 0).saturating_mul(x.into())) + // Standard Error: 833 + .saturating_add(Weight::from_parts(39_753, 0).saturating_mul(y.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 23).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(y.into())) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:1 w:1) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `x` is `[0, 350]`. /// The range of component `y` is `[0, 349]`. /// The range of component `z` is `[0, 99]`. fn delegate_with_auto_compound(x: u32, y: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + x * (60 ±0) + y * (21 ±0) + z * (78 ±0)` - // Estimated: `26419 + x * (44 ±0) + y * (19 ±0) + z * (76 ±1)` - // Minimum execution time: 90_000_000 picoseconds. - Weight::from_parts(90_069_327, 26419) - // Standard Error: 2_700 - .saturating_add(Weight::from_parts(128_216, 0).saturating_mul(x.into())) - // Standard Error: 2_707 - .saturating_add(Weight::from_parts(7_494, 0).saturating_mul(y.into())) - // Standard Error: 9_535 - .saturating_add(Weight::from_parts(138_309, 0).saturating_mul(z.into())) + // Estimated: `26253 + x * (44 ±0) + y * (19 ±0) + z * (76 ±1)` + // Minimum execution time: 130_103_000 picoseconds. + Weight::from_parts(116_616_415, 26253) + // Standard Error: 1_141 + .saturating_add(Weight::from_parts(117_041, 0).saturating_mul(x.into())) + // Standard Error: 4_030 + .saturating_add(Weight::from_parts(178_483, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) .saturating_add(Weight::from_parts(0, 44).saturating_mul(x.into())) .saturating_add(Weight::from_parts(0, 19).saturating_mul(y.into())) .saturating_add(Weight::from_parts(0, 76).saturating_mul(z.into())) } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegatorState` (r:2 w:2) - /// Proof: `ParachainStaking::DelegatorState` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AutoCompoundingDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::AutoCompoundingDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TopDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::TopDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::BottomDelegations` (r:1 w:1) - /// Proof: `ParachainStaking::BottomDelegations` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:2 w:2) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1287), added: 3762, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:2 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `ParachainStaking::DelegationScheduledRequests` (r:1 w:1) - /// Proof: `ParachainStaking::DelegationScheduledRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Total` (r:1 w:1) - /// Proof: `ParachainStaking::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegatorState (r:2 w:2) + /// Proof Skipped: ParachainStaking DelegatorState (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AutoCompoundingDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking AutoCompoundingDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking TopDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking TopDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking BottomDelegations (r:1 w:1) + /// Proof Skipped: ParachainStaking BottomDelegations (max_values: None, max_size: None, mode: Measured) + /// Storage: Balances Locks (r:2 w:2) + /// Proof: Balances Locks (max_values: None, max_size: Some(1287), added: 3762, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:2 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(37), added: 2512, mode: MaxEncodedLen) + /// Storage: ParachainStaking DelegationScheduledRequests (r:1 w:1) + /// Proof Skipped: ParachainStaking DelegationScheduledRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking Total (r:1 w:1) + /// Proof Skipped: ParachainStaking Total (max_values: Some(1), max_size: None, mode: Measured) fn delegate_with_auto_compound_worst() -> Weight { // Proof Size summary in bytes: - // Measured: `48446` - // Estimated: `54386` - // Minimum execution time: 213_000_000 picoseconds. - Weight::from_parts(242_000_000, 54386) + // Measured: `48167` + // Estimated: `54107` + // Minimum execution time: 276_584_000 picoseconds. + Weight::from_parts(279_594_000, 54107) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(116), added: 2591, mode: MaxEncodedLen) fn mint_collator_reward() -> Weight { // Proof Size summary in bytes: - // Measured: `91` + // Measured: `128` // Estimated: `3581` - // Minimum execution time: 22_000_000 picoseconds. - Weight::from_parts(23_000_000, 3581) + // Minimum execution time: 28_741_000 picoseconds. + Weight::from_parts(29_344_000, 3581) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1825,29 +1732,30 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `ParachainStaking::EnableMarkingOffline` (r:1 w:0) - /// Proof: `ParachainStaking::EnableMarkingOffline` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::TotalSelected` (r:1 w:0) - /// Proof: `ParachainStaking::TotalSelected` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::SelectedCandidates` (r:1 w:0) - /// Proof: `ParachainStaking::SelectedCandidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::Round` (r:1 w:0) - /// Proof: `ParachainStaking::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AtStake` (r:1 w:0) - /// Proof: `ParachainStaking::AtStake` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::AwardedPts` (r:1 w:0) - /// Proof: `ParachainStaking::AwardedPts` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidateInfo` (r:1 w:1) - /// Proof: `ParachainStaking::CandidateInfo` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParachainStaking::CandidatePool` (r:1 w:1) - /// Proof: `ParachainStaking::CandidatePool` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: ParachainStaking EnableMarkingOffline (r:1 w:0) + /// Proof Skipped: ParachainStaking EnableMarkingOffline (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking TotalSelected (r:1 w:0) + /// Proof Skipped: ParachainStaking TotalSelected (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking SelectedCandidates (r:1 w:0) + /// Proof Skipped: ParachainStaking SelectedCandidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainStaking AtStake (r:2 w:0) + /// Proof Skipped: ParachainStaking AtStake (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking AwardedPts (r:2 w:0) + /// Proof Skipped: ParachainStaking AwardedPts (max_values: None, max_size: None, mode: Measured) + /// Storage: MoonbeamOrbiters OrbiterPerRound (r:1 w:0) + /// Proof Skipped: MoonbeamOrbiters OrbiterPerRound (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidateInfo (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidateInfo (max_values: None, max_size: None, mode: Measured) + /// Storage: ParachainStaking CandidatePool (r:1 w:1) + /// Proof Skipped: ParachainStaking CandidatePool (max_values: Some(1), max_size: None, mode: Measured) fn notify_inactive_collator() -> Weight { // Proof Size summary in bytes: - // Measured: `11709` - // Estimated: `15174` - // Minimum execution time: 59_000_000 picoseconds. - Weight::from_parts(74_000_000, 15174) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `11494` + // Estimated: `17434` + // Minimum execution time: 41_130_000 picoseconds. + Weight::from_parts(41_130_000, 0) + .saturating_add(Weight::from_parts(0, 17434)) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } -} \ No newline at end of file +} diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index 1c8c3c62f..8151835a5 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -224,7 +224,6 @@ impl pallet_parachain_staking::Config for Runtime { type SlotProvider = StakingRoundSlotProvider; type WeightInfo = (); type MaxCandidates = MaxCandidates; - type SlotsPerYear = frame_support::traits::ConstU32<{ 31_557_600 / 6 }>; } pub(crate) struct ExtBuilder { diff --git a/runtime/laos/src/configs/parachain_staking.rs b/runtime/laos/src/configs/parachain_staking.rs index 6f4fa8b4c..9e65bf68d 100644 --- a/runtime/laos/src/configs/parachain_staking.rs +++ b/runtime/laos/src/configs/parachain_staking.rs @@ -25,9 +25,6 @@ use pallet_session::{SessionManager, ShouldEndSession}; use sp_consensus_slots::Slot; use sp_staking::SessionIndex; -const SECONDS_PER_YEAR: u32 = 31_557_600; -const SECONDS_PER_BLOCK: u32 = MILLISECS_PER_BLOCK as u32 / 1000; - // Define runtime constants used across the parachain staking configuration. parameter_types! { pub const MinCandidateStk: u128 = 20_000 * UNIT; // Minimum stake to be a staking candidate. @@ -45,7 +42,8 @@ parameter_types! { pub const MaxDelegationsPerDelegator: u32 = 100; // Max delegations per delegator. pub const MinDelegation: u128 = 500 * UNIT; // Minimum stake to be a delegator. pub const MaxCandidates: u32 = 200; // Max candidates allowed. - pub const SlotsPerYear: u32 = SECONDS_PER_YEAR / SECONDS_PER_BLOCK; // Number of slots per year. + pub const SlotDuration: u64 = MILLISECS_PER_BLOCK; + pub const BlockTime: u64 = MILLISECS_PER_BLOCK; } // Implementing the configuration trait for the parachain staking pallet. @@ -75,7 +73,8 @@ impl StakingConfig for Runtime { type OnNewRound = (); // Placeholder for future implementation. type SlotProvider = StakingRoundSlotProvider; type MaxCandidates = MaxCandidates; - type SlotsPerYear = SlotsPerYear; + type SlotDuration = SlotDuration; + type BlockTime = BlockTime; type WeightInfo = weights::pallet_parachain_staking::WeightInfo; } @@ -173,7 +172,7 @@ impl frame_support::traits::EstimateNextSessionRotation for Paracha mod tests { use super::{MinCandidateStk, MinDelegation, MinSelectedCandidates}; use crate::{ - configs::parachain_staking::{MinBlocksPerRound, ParachainStakingAdapter, SlotsPerYear}, + configs::parachain_staking::{MinBlocksPerRound, ParachainStakingAdapter}, tests::ExtBuilder, Balances, ParachainStaking, RuntimeOrigin, System, }; @@ -340,9 +339,4 @@ mod tests { assert_eq!(next_session, Some(MinBlocksPerRound::get())); }); } - - #[test] - fn test_slot_per_year() { - assert_eq!(SlotsPerYear::get(), 31_557_600 / 12); - } }