[Enhancement] Convert fast-unstake to use StakingInterface, decouplin… (#12424)

* [Enhancement] Convert fast-unstake to use StakingInterface, decoupling it from Staking

* Update primitives/staking/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update primitives/staking/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* fix validator comment

* remove todo

* ideas from Kian (#12474)

Co-authored-by: kianenigma <kian@parity.io>

* Rename StakingInterface -> Staking for nomination-pools

* Staking fixes

* StakingInterface changes

* fix fast-unstake

* fix nomination-pools

* Fix fast-unstake tests

* Fix benches for fast-unstake

* fix is_unbonding

* fix nomination pools

* fix node code

* add mock comments

* fix imports

* remove todo

* more fixes

* more fixes

* bench fixes

* more fixes

* more fixes

* import fix

* more fixes

* more bench fix

* refix

* refix

* Update primitives/staking/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* is_unbonding returns a result

* fix

* review fixes

* more review fixes

* more fixes

* more fixes

* Update frame/fast-unstake/src/benchmarking.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* remove redundant CurrencyBalance from nom-pools

* remove CB

* rephrase

* Apply suggestions from code review

* Update frame/nomination-pools/src/tests.rs

* finish damn renamed

* clippy fix

* fix

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: kianenigma <kian@parity.io>
Co-authored-by: parity-processbot <>
Co-authored-by: Squirrel <gilescope@gmail.com>
This commit is contained in:
Roman Useinov
2022-10-29 11:22:58 +02:00
committed by GitHub
parent 3faa0bd20d
commit 4bc091f4a9
16 changed files with 371 additions and 304 deletions
+3 -3
View File
@@ -585,7 +585,8 @@ impl pallet_fast_unstake::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type ControlOrigin = frame_system::EnsureRoot<AccountId>;
type Deposit = ConstU128<{ DOLLARS }>;
type DepositCurrency = Balances;
type Currency = Balances;
type Staking = Staking;
type WeightInfo = ();
}
@@ -773,11 +774,10 @@ impl pallet_nomination_pools::Config for Runtime {
type WeightInfo = ();
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = pallet_staking::Pallet<Self>;
type Staking = Staking;
type PostUnbondingPoolsWindow = PostUnbondPoolsWindow;
type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>;
+5 -8
View File
@@ -24,10 +24,6 @@ sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/
sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" }
sp-staking = { default-features = false, path = "../../primitives/staking" }
pallet-balances = { default-features = false, path = "../balances" }
pallet-timestamp = { default-features = false, path = "../timestamp" }
pallet-staking = { default-features = false, path = "../staking" }
frame-election-provider-support = { default-features = false, path = "../election-provider-support" }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
@@ -37,6 +33,10 @@ pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../staking/reward
sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" }
substrate-test-utils = { version = "4.0.0-dev", path = "../../test-utils" }
sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" }
pallet-staking = { path = "../staking" }
pallet-balances = { path = "../balances" }
pallet-timestamp = { path = "../timestamp" }
[features]
default = ["std"]
@@ -53,9 +53,6 @@ std = [
"sp-runtime/std",
"sp-std/std",
"pallet-staking/std",
"pallet-balances/std",
"pallet-timestamp/std",
"frame-election-provider-support/std",
"frame-benchmarking/std",
@@ -63,6 +60,6 @@ std = [
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-staking/runtime-benchmarks",
"sp-staking/runtime-benchmarks"
]
try-runtime = ["frame-support/try-runtime"]
@@ -26,22 +26,15 @@ use frame_support::{
traits::{Currency, EnsureOrigin, Get, Hooks},
};
use frame_system::RawOrigin;
use pallet_staking::Pallet as Staking;
use sp_runtime::traits::{StaticLookup, Zero};
use sp_staking::EraIndex;
use sp_runtime::traits::Zero;
use sp_staking::{EraIndex, StakingInterface};
use sp_std::prelude::*;
const USER_SEED: u32 = 0;
const DEFAULT_BACKER_PER_VALIDATOR: u32 = 128;
const MAX_VALIDATORS: u32 = 128;
type CurrencyOf<T> = <T as pallet_staking::Config>::Currency;
fn l<T: Config>(
who: T::AccountId,
) -> <<T as frame_system::Config>::Lookup as StaticLookup>::Source {
T::Lookup::unlookup(who)
}
type CurrencyOf<T> = <T as Config>::Currency;
fn create_unexposed_nominator<T: Config>() -> T::AccountId {
let account = frame_benchmarking::account::<T::AccountId>("nominator_42", 0, USER_SEED);
@@ -53,18 +46,9 @@ fn fund_and_bond_account<T: Config>(account: &T::AccountId) {
let stake = CurrencyOf::<T>::minimum_balance() * 100u32.into();
CurrencyOf::<T>::make_free_balance_be(&account, stake * 10u32.into());
let account_lookup = l::<T>(account.clone());
// bond and nominate ourselves, this will guarantee that we are not backing anyone.
assert_ok!(Staking::<T>::bond(
RawOrigin::Signed(account.clone()).into(),
account_lookup.clone(),
stake,
pallet_staking::RewardDestination::Controller,
));
assert_ok!(Staking::<T>::nominate(
RawOrigin::Signed(account.clone()).into(),
vec![account_lookup]
));
assert_ok!(T::Staking::bond(account, stake, account));
assert_ok!(T::Staking::nominate(account, vec![account.clone()]));
}
pub(crate) fn fast_unstake_events<T: Config>() -> Vec<crate::Event<T>> {
@@ -91,13 +75,11 @@ fn setup_staking<T: Config>(v: u32, until: EraIndex) {
.map(|s| {
let who = frame_benchmarking::account::<T::AccountId>("nominator", era, s);
let value = ed;
pallet_staking::IndividualExposure { who, value }
(who, value)
})
.collect::<Vec<_>>();
let exposure =
pallet_staking::Exposure { total: Default::default(), own: Default::default(), others };
validators.iter().for_each(|v| {
Staking::<T>::add_era_stakers(era, v.clone(), exposure.clone());
T::Staking::add_era_stakers(&era, &v, others.clone());
});
}
}
@@ -137,13 +119,13 @@ benchmarks! {
// on_idle, when we check some number of eras,
on_idle_check {
// number of eras multiplied by validators in that era.
let x in (<T as pallet_staking::Config>::BondingDuration::get() * 1) .. (<T as pallet_staking::Config>::BondingDuration::get() * MAX_VALIDATORS);
let x in (T::Staking::bonding_duration() * 1) .. (T::Staking::bonding_duration() * MAX_VALIDATORS);
let v = x / <T as pallet_staking::Config>::BondingDuration::get();
let u = <T as pallet_staking::Config>::BondingDuration::get();
let u = T::Staking::bonding_duration();
let v = x / u;
ErasToCheckPerBlock::<T>::put(u);
pallet_staking::CurrentEra::<T>::put(u);
T::Staking::set_current_era(u);
// setup staking with v validators and u eras of data (0..=u)
setup_staking::<T>(v, u);
+34 -51
View File
@@ -80,18 +80,16 @@ macro_rules! log {
pub mod pallet {
use super::*;
use crate::types::*;
use frame_election_provider_support::ElectionProviderBase;
use frame_support::{
pallet_prelude::*,
traits::{Defensive, ReservableCurrency},
};
use frame_system::{pallet_prelude::*, RawOrigin};
use pallet_staking::Pallet as Staking;
use frame_system::pallet_prelude::*;
use sp_runtime::{
traits::{Saturating, Zero},
DispatchResult,
};
use sp_staking::EraIndex;
use sp_staking::{EraIndex, StakingInterface};
use sp_std::{prelude::*, vec::Vec};
pub use weights::WeightInfo;
@@ -101,7 +99,7 @@ pub mod pallet {
pub struct MaxChecking<T: Config>(sp_std::marker::PhantomData<T>);
impl<T: Config> frame_support::traits::Get<u32> for MaxChecking<T> {
fn get() -> u32 {
<T as pallet_staking::Config>::BondingDuration::get() + 1
T::Staking::bonding_duration() + 1
}
}
@@ -109,14 +107,14 @@ pub mod pallet {
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config + pallet_staking::Config {
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
+ TryInto<Event<Self>>;
/// The currency used for deposits.
type DepositCurrency: ReservableCurrency<Self::AccountId, Balance = BalanceOf<Self>>;
type Currency: ReservableCurrency<Self::AccountId>;
/// Deposit to take for unstaking, to make sure we're able to slash the it in order to cover
/// the costs of resources on unsuccessful unstake.
@@ -125,6 +123,9 @@ pub mod pallet {
/// The origin that can control this pallet.
type ControlOrigin: frame_support::traits::EnsureOrigin<Self::RuntimeOrigin>;
/// The access to staking functionality.
type Staking: StakingInterface<Balance = BalanceOf<Self>, AccountId = Self::AccountId>;
/// The weight information of this pallet.
type WeightInfo: WeightInfo;
}
@@ -221,28 +222,25 @@ pub mod pallet {
let ctrl = ensure_signed(origin)?;
ensure!(ErasToCheckPerBlock::<T>::get() != 0, <Error<T>>::CallNotAllowed);
let ledger =
pallet_staking::Ledger::<T>::get(&ctrl).ok_or(Error::<T>::NotController)?;
ensure!(!Queue::<T>::contains_key(&ledger.stash), Error::<T>::AlreadyQueued);
let stash_account =
T::Staking::stash_by_ctrl(&ctrl).map_err(|_| Error::<T>::NotController)?;
ensure!(!Queue::<T>::contains_key(&stash_account), Error::<T>::AlreadyQueued);
ensure!(
Head::<T>::get().map_or(true, |UnstakeRequest { stash, .. }| stash != ledger.stash),
Head::<T>::get()
.map_or(true, |UnstakeRequest { stash, .. }| stash_account != stash),
Error::<T>::AlreadyHead
);
// second part of the && is defensive.
ensure!(
ledger.active == ledger.total && ledger.unlocking.is_empty(),
Error::<T>::NotFullyBonded
);
ensure!(!T::Staking::is_unbonding(&stash_account)?, Error::<T>::NotFullyBonded);
// chill and fully unstake.
Staking::<T>::chill(RawOrigin::Signed(ctrl.clone()).into())?;
Staking::<T>::unbond(RawOrigin::Signed(ctrl).into(), ledger.total)?;
T::Staking::chill(&stash_account)?;
T::Staking::fully_unbond(&stash_account)?;
T::DepositCurrency::reserve(&ledger.stash, T::Deposit::get())?;
T::Currency::reserve(&stash_account, T::Deposit::get())?;
// enqueue them.
Queue::<T>::insert(ledger.stash, T::Deposit::get());
Queue::<T>::insert(stash_account, T::Deposit::get());
Ok(())
}
@@ -259,18 +257,18 @@ pub mod pallet {
ensure!(ErasToCheckPerBlock::<T>::get() != 0, <Error<T>>::CallNotAllowed);
let stash = pallet_staking::Ledger::<T>::get(&ctrl)
.map(|l| l.stash)
.ok_or(Error::<T>::NotController)?;
ensure!(Queue::<T>::contains_key(&stash), Error::<T>::NotQueued);
let stash_account =
T::Staking::stash_by_ctrl(&ctrl).map_err(|_| Error::<T>::NotController)?;
ensure!(Queue::<T>::contains_key(&stash_account), Error::<T>::NotQueued);
ensure!(
Head::<T>::get().map_or(true, |UnstakeRequest { stash, .. }| stash != stash),
Head::<T>::get()
.map_or(true, |UnstakeRequest { stash, .. }| stash_account != stash),
Error::<T>::AlreadyHead
);
let deposit = Queue::<T>::take(stash.clone());
let deposit = Queue::<T>::take(stash_account.clone());
if let Some(deposit) = deposit.defensive() {
let remaining = T::DepositCurrency::unreserve(&stash, deposit);
let remaining = T::Currency::unreserve(&stash_account, deposit);
if !remaining.is_zero() {
frame_support::defensive!("`not enough balance to unreserve`");
ErasToCheckPerBlock::<T>::put(0);
@@ -314,7 +312,7 @@ pub mod pallet {
// NOTE: here we're assuming that the number of validators has only ever increased,
// meaning that the number of exposures to check is either this per era, or less.
let validator_count = pallet_staking::ValidatorCount::<T>::get();
let validator_count = T::Staking::desired_validator_count();
// determine the number of eras to check. This is based on both `ErasToCheckPerBlock`
// and `remaining_weight` passed on to us from the runtime executive.
@@ -330,8 +328,7 @@ pub mod pallet {
}
}
if <<T as pallet_staking::Config>::ElectionProvider as ElectionProviderBase>::ongoing()
{
if T::Staking::election_ongoing() {
// NOTE: we assume `ongoing` does not consume any weight.
// there is an ongoing election -- we better not do anything. Imagine someone is not
// exposed anywhere in the last era, and the snapshot for the election is already
@@ -366,8 +363,8 @@ pub mod pallet {
);
// the range that we're allowed to check in this round.
let current_era = pallet_staking::CurrentEra::<T>::get().unwrap_or_default();
let bonding_duration = <T as pallet_staking::Config>::BondingDuration::get();
let current_era = T::Staking::current_era();
let bonding_duration = T::Staking::bonding_duration();
// prune all the old eras that we don't care about. This will help us keep the bound
// of `checked`.
checked.retain(|e| *e >= current_era.saturating_sub(bonding_duration));
@@ -401,16 +398,9 @@ pub mod pallet {
);
if unchecked_eras_to_check.is_empty() {
// `stash` is not exposed in any era now -- we can let go of them now.
let num_slashing_spans = Staking::<T>::slashing_spans(&stash).iter().count() as u32;
let result = T::Staking::force_unstake(stash.clone());
let result = pallet_staking::Pallet::<T>::force_unstake(
RawOrigin::Root.into(),
stash.clone(),
num_slashing_spans,
);
let remaining = T::DepositCurrency::unreserve(&stash, deposit);
let remaining = T::Currency::unreserve(&stash, deposit);
if !remaining.is_zero() {
frame_support::defensive!("`not enough balance to unreserve`");
ErasToCheckPerBlock::<T>::put(0);
@@ -426,7 +416,7 @@ pub mod pallet {
let mut eras_checked = 0u32;
let is_exposed = unchecked_eras_to_check.iter().any(|e| {
eras_checked.saturating_inc();
Self::is_exposed_in_era(&stash, e)
T::Staking::is_exposed_in_era(&stash, e)
});
log!(
@@ -442,7 +432,7 @@ pub mod pallet {
// the last 28 eras, have registered yourself to be unstaked, midway being checked,
// you are exposed.
if is_exposed {
T::DepositCurrency::slash_reserved(&stash, deposit);
T::Currency::slash_reserved(&stash, deposit);
log!(info, "slashed {:?} by {:?}", stash, deposit);
Self::deposit_event(Event::<T>::Slashed { stash, amount: deposit });
} else {
@@ -472,12 +462,5 @@ pub mod pallet {
<T as Config>::WeightInfo::on_idle_check(validator_count * eras_checked)
}
}
/// Checks whether an account `staker` has been exposed in an era.
fn is_exposed_in_era(staker: &T::AccountId, era: &EraIndex) -> bool {
pallet_staking::ErasStakers::<T>::iter_prefix(era).any(|(validator, exposures)| {
validator == *staker || exposures.others.iter().any(|i| i.who == *staker)
})
}
}
}
+2 -1
View File
@@ -174,7 +174,8 @@ parameter_types! {
impl fast_unstake::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Deposit = DepositAmount;
type DepositCurrency = Balances;
type Currency = Balances;
type Staking = Staking;
type ControlOrigin = frame_system::EnsureRoot<Self::AccountId>;
type WeightInfo = ();
}
+7 -7
View File
@@ -48,7 +48,7 @@ fn register_insufficient_funds_fails() {
use pallet_balances::Error as BalancesError;
ExtBuilder::default().build_and_execute(|| {
ErasToCheckPerBlock::<T>::put(1);
<T as Config>::DepositCurrency::make_free_balance_be(&1, 3);
<T as Config>::Currency::make_free_balance_be(&1, 3);
// Controller account registers for fast unstake.
assert_noop!(
@@ -138,15 +138,15 @@ fn deregister_works() {
ExtBuilder::default().build_and_execute(|| {
ErasToCheckPerBlock::<T>::put(1);
assert_eq!(<T as Config>::DepositCurrency::reserved_balance(&1), 0);
assert_eq!(<T as Config>::Currency::reserved_balance(&1), 0);
// Controller account registers for fast unstake.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(<T as Config>::DepositCurrency::reserved_balance(&1), DepositAmount::get());
assert_eq!(<T as Config>::Currency::reserved_balance(&1), DepositAmount::get());
// Controller then changes mind and deregisters.
assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(2)));
assert_eq!(<T as Config>::DepositCurrency::reserved_balance(&1), 0);
assert_eq!(<T as Config>::Currency::reserved_balance(&1), 0);
// Ensure stash no longer exists in the queue.
assert_eq!(Queue::<T>::get(1), None);
@@ -363,7 +363,7 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// given
assert_eq!(<T as Config>::DepositCurrency::reserved_balance(&1), 0);
assert_eq!(<T as Config>::Currency::reserved_balance(&1), 0);
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4)));
@@ -371,7 +371,7 @@ mod on_idle {
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(8)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(10)));
assert_eq!(<T as Config>::DepositCurrency::reserved_balance(&1), DepositAmount::get());
assert_eq!(<T as Config>::Currency::reserved_balance(&1), DepositAmount::get());
assert_eq!(Queue::<T>::count(), 5);
assert_eq!(Head::<T>::get(), None);
@@ -411,7 +411,7 @@ mod on_idle {
);
assert_eq!(Queue::<T>::count(), 3);
assert_eq!(<T as Config>::DepositCurrency::reserved_balance(&1), 0);
assert_eq!(<T as Config>::Currency::reserved_balance(&1), 0);
assert_eq!(
fast_unstake_events_since_last_call(),
+3 -4
View File
@@ -17,6 +17,7 @@
//! Types used in the Fast Unstake pallet.
use crate::Config;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
traits::{Currency, Get},
@@ -26,10 +27,8 @@ use scale_info::TypeInfo;
use sp_staking::EraIndex;
use sp_std::{fmt::Debug, prelude::*};
pub type BalanceOf<T> = <<T as pallet_staking::Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::Balance;
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
/// An unstake request.
#[derive(
Encode, Decode, EqNoBound, PartialEqNoBound, Clone, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen,
@@ -133,15 +133,15 @@ impl<T: Config> ListScenario<T> {
// Create accounts with the origin weight
let (pool_creator1, pool_origin1) = create_pool_account::<T>(USER_SEED + 1, origin_weight);
T::StakingInterface::nominate(
pool_origin1.clone(),
T::Staking::nominate(
&pool_origin1,
// NOTE: these don't really need to be validators.
vec![account("random_validator", 0, USER_SEED)],
)?;
let (_, pool_origin2) = create_pool_account::<T>(USER_SEED + 2, origin_weight);
T::StakingInterface::nominate(
pool_origin2.clone(),
T::Staking::nominate(
&pool_origin2,
vec![account("random_validator", 0, USER_SEED)].clone(),
)?;
@@ -156,10 +156,7 @@ impl<T: Config> ListScenario<T> {
// Create an account with the worst case destination weight
let (_, pool_dest1) = create_pool_account::<T>(USER_SEED + 3, dest_weight);
T::StakingInterface::nominate(
pool_dest1.clone(),
vec![account("random_validator", 0, USER_SEED)],
)?;
T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?;
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin1)).unwrap(), origin_weight);
@@ -185,12 +182,11 @@ impl<T: Config> ListScenario<T> {
self.origin1_member = Some(joiner.clone());
CurrencyOf::<T>::make_free_balance_be(&joiner, amount * 2u32.into());
let original_bonded = T::StakingInterface::active_stake(&self.origin1).unwrap();
let original_bonded = T::Staking::active_stake(&self.origin1).unwrap();
// Unbond `amount` from the underlying pool account so when the member joins
// we will maintain `current_bonded`.
T::StakingInterface::unbond(self.origin1.clone(), amount)
.expect("the pool was created in `Self::new`.");
T::Staking::unbond(&self.origin1, amount).expect("the pool was created in `Self::new`.");
// Account pool points for the unbonded balance.
BondedPools::<T>::mutate(&1, |maybe_pool| {
@@ -219,7 +215,7 @@ frame_benchmarking::benchmarks! {
// setup the worst case list scenario.
let scenario = ListScenario::<T>::new(origin_weight, true)?;
assert_eq!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap(),
T::Staking::active_stake(&scenario.origin1).unwrap(),
origin_weight
);
@@ -234,7 +230,7 @@ frame_benchmarking::benchmarks! {
verify {
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), joiner_free - max_additional);
assert_eq!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap(),
T::Staking::active_stake(&scenario.origin1).unwrap(),
scenario.dest_weight
);
}
@@ -249,7 +245,7 @@ frame_benchmarking::benchmarks! {
}: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra))
verify {
assert!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap() >=
T::Staking::active_stake(&scenario.origin1).unwrap() >=
scenario.dest_weight
);
}
@@ -267,7 +263,7 @@ frame_benchmarking::benchmarks! {
}: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::Rewards)
verify {
assert!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap() >=
T::Staking::active_stake(&scenario.origin1).unwrap() >=
scenario.dest_weight
);
}
@@ -314,7 +310,7 @@ frame_benchmarking::benchmarks! {
whitelist_account!(member_id);
}: _(RuntimeOrigin::Signed(member_id.clone()), member_id_lookup, all_points)
verify {
let bonded_after = T::StakingInterface::active_stake(&scenario.origin1).unwrap();
let bonded_after = T::Staking::active_stake(&scenario.origin1).unwrap();
// We at least went down to the destination bag
assert!(bonded_after <= scenario.dest_weight);
let member = PoolMembers::<T>::get(
@@ -323,7 +319,7 @@ frame_benchmarking::benchmarks! {
.unwrap();
assert_eq!(
member.unbonding_eras.keys().cloned().collect::<Vec<_>>(),
vec![0 + T::StakingInterface::bonding_duration()]
vec![0 + T::Staking::bonding_duration()]
);
assert_eq!(
member.unbonding_eras.values().cloned().collect::<Vec<_>>(),
@@ -345,7 +341,7 @@ frame_benchmarking::benchmarks! {
// Sanity check join worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond + min_join_bond
);
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), min_join_bond);
@@ -355,7 +351,7 @@ frame_benchmarking::benchmarks! {
// Sanity check that unbond worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond
);
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
@@ -388,7 +384,7 @@ frame_benchmarking::benchmarks! {
// Sanity check join worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond + min_join_bond
);
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), min_join_bond);
@@ -399,7 +395,7 @@ frame_benchmarking::benchmarks! {
// Sanity check that unbond worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond
);
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
@@ -446,7 +442,7 @@ frame_benchmarking::benchmarks! {
// Sanity check that unbond worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
T::Staking::active_stake(&pool_account).unwrap(),
Zero::zero()
);
assert_eq!(
@@ -525,8 +521,8 @@ frame_benchmarking::benchmarks! {
}
);
assert_eq!(
T::StakingInterface::active_stake(&Pools::<T>::create_bonded_account(1)),
Some(min_create_bond)
T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
Ok(min_create_bond)
);
}
@@ -564,8 +560,8 @@ frame_benchmarking::benchmarks! {
}
);
assert_eq!(
T::StakingInterface::active_stake(&Pools::<T>::create_bonded_account(1)),
Some(min_create_bond)
T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
Ok(min_create_bond)
);
}
@@ -647,13 +643,13 @@ frame_benchmarking::benchmarks! {
.map(|i| account("stash", USER_SEED, i))
.collect();
assert_ok!(Pools::<T>::nominate(RuntimeOrigin::Signed(depositor.clone()).into(), 1, validators));
assert!(T::StakingInterface::nominations(Pools::<T>::create_bonded_account(1)).is_some());
assert_ok!(T::Staking::nominate(&pool_account, validators));
assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_some());
whitelist_account!(depositor);
}:_(RuntimeOrigin::Signed(depositor.clone()), 1)
verify {
assert!(T::StakingInterface::nominations(Pools::<T>::create_bonded_account(1)).is_none());
assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_none());
}
impl_benchmark_test_suite!(
@@ -157,11 +157,10 @@ impl pallet_nomination_pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = Staking;
type Staking = Staking;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>;
+27 -52
View File
@@ -286,7 +286,7 @@ use sp_runtime::{
AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup,
Zero,
},
FixedPointNumber, FixedPointOperand,
FixedPointNumber,
};
use sp_staking::{EraIndex, OnStakerSlash, StakingInterface};
use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec};
@@ -417,7 +417,7 @@ impl<T: Config> PoolMember<T> {
///
/// This is derived from the ratio of points in the pool to which the member belongs to.
/// Might return different values based on the pool state for the same member and points.
fn active_balance(&self) -> BalanceOf<T> {
fn active_stake(&self) -> BalanceOf<T> {
if let Some(pool) = BondedPool::<T>::get(self.pool_id).defensive() {
pool.points_to_balance(self.points)
} else {
@@ -623,7 +623,7 @@ impl<T: Config> BondedPool<T> {
/// This is often used for bonding and issuing new funds into the pool.
fn balance_to_point(&self, new_funds: BalanceOf<T>) -> BalanceOf<T> {
let bonded_balance =
T::StakingInterface::active_stake(&self.bonded_account()).unwrap_or(Zero::zero());
T::Staking::active_stake(&self.bonded_account()).unwrap_or(Zero::zero());
Pallet::<T>::balance_to_point(bonded_balance, self.points, new_funds)
}
@@ -632,7 +632,7 @@ impl<T: Config> BondedPool<T> {
/// This is often used for unbonding.
fn points_to_balance(&self, points: BalanceOf<T>) -> BalanceOf<T> {
let bonded_balance =
T::StakingInterface::active_stake(&self.bonded_account()).unwrap_or(Zero::zero());
T::Staking::active_stake(&self.bonded_account()).unwrap_or(Zero::zero());
Pallet::<T>::point_to_balance(bonded_balance, self.points, points)
}
@@ -683,7 +683,7 @@ impl<T: Config> BondedPool<T> {
fn transferrable_balance(&self) -> BalanceOf<T> {
let account = self.bonded_account();
T::Currency::free_balance(&account)
.saturating_sub(T::StakingInterface::active_stake(&account).unwrap_or_default())
.saturating_sub(T::Staking::active_stake(&account).unwrap_or_default())
}
fn is_root(&self, who: &T::AccountId) -> bool {
@@ -738,7 +738,7 @@ impl<T: Config> BondedPool<T> {
ensure!(!self.is_destroying(), Error::<T>::CanNotChangeState);
let bonded_balance =
T::StakingInterface::active_stake(&self.bonded_account()).unwrap_or(Zero::zero());
T::Staking::active_stake(&self.bonded_account()).unwrap_or(Zero::zero());
ensure!(!bonded_balance.is_zero(), Error::<T>::OverflowRisk);
let points_to_balance_ratio_floor = self
@@ -791,7 +791,7 @@ impl<T: Config> BondedPool<T> {
target_member.active_points().saturating_sub(unbonding_points);
let mut target_member_after_unbond = (*target_member).clone();
target_member_after_unbond.points = new_depositor_points;
target_member_after_unbond.active_balance()
target_member_after_unbond.active_stake()
};
// any partial unbonding is only ever allowed if this unbond is permissioned.
@@ -862,8 +862,8 @@ impl<T: Config> BondedPool<T> {
/// Bond exactly `amount` from `who`'s funds into this pool.
///
/// If the bond type is `Create`, `StakingInterface::bond` is called, and `who`
/// is allowed to be killed. Otherwise, `StakingInterface::bond_extra` is called and `who`
/// If the bond type is `Create`, `Staking::bond` is called, and `who`
/// is allowed to be killed. Otherwise, `Staking::bond_extra` is called and `who`
/// cannot be killed.
///
/// Returns `Ok(points_issues)`, `Err` otherwise.
@@ -889,16 +889,11 @@ impl<T: Config> BondedPool<T> {
let points_issued = self.issue(amount);
match ty {
BondType::Create => T::StakingInterface::bond(
bonded_account.clone(),
bonded_account,
amount,
self.reward_account(),
)?,
BondType::Create => T::Staking::bond(&bonded_account, amount, &self.reward_account())?,
// The pool should always be created in such a way its in a state to bond extra, but if
// the active balance is slashed below the minimum bonded or the account cannot be
// found, we exit early.
BondType::Later => T::StakingInterface::bond_extra(bonded_account, amount)?,
BondType::Later => T::Staking::bond_extra(&bonded_account, amount)?,
}
Ok(points_issued)
@@ -1129,7 +1124,7 @@ impl<T: Config> Get<u32> for TotalUnbondingPools<T> {
// NOTE: this may be dangerous in the scenario bonding_duration gets decreased because
// we would no longer be able to decode `UnbondingPoolsWithEra`, which uses
// `TotalUnbondingPools` as the bound
T::StakingInterface::bonding_duration() + T::PostUnbondingPoolsWindow::get()
T::Staking::bonding_duration() + T::PostUnbondingPoolsWindow::get()
}
}
@@ -1138,7 +1133,6 @@ pub mod pallet {
use super::*;
use frame_support::traits::StorageVersion;
use frame_system::{ensure_signed, pallet_prelude::*};
use sp_runtime::traits::CheckedAdd;
/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);
@@ -1157,20 +1151,7 @@ pub mod pallet {
type WeightInfo: weights::WeightInfo;
/// The nominating balance.
type Currency: Currency<Self::AccountId, Balance = Self::CurrencyBalance>;
/// Sadly needed to bound it to `FixedPointOperand`.
// The only alternative is to sprinkle a `where BalanceOf<T>: FixedPointOperand` in roughly
// a million places, so we prefer doing this.
type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
+ codec::FullCodec
+ MaybeSerializeDeserialize
+ sp_std::fmt::Debug
+ Default
+ FixedPointOperand
+ CheckedAdd
+ TypeInfo
+ MaxEncodedLen;
type Currency: Currency<Self::AccountId>;
/// The type that is used for reward counter.
///
@@ -1212,10 +1193,7 @@ pub mod pallet {
type U256ToBalance: Convert<U256, BalanceOf<Self>>;
/// The interface for nominating.
type StakingInterface: StakingInterface<
Balance = BalanceOf<Self>,
AccountId = Self::AccountId,
>;
type Staking: StakingInterface<Balance = BalanceOf<Self>, AccountId = Self::AccountId>;
/// The amount of eras a `SubPools::with_era` pool can exist before it gets merged into the
/// `SubPools::no_era` pool. In other words, this is the amount of eras a member will be
@@ -1650,12 +1628,12 @@ pub mod pallet {
let _ = reward_pool.update_records(bonded_pool.id, bonded_pool.points)?;
let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?;
let current_era = T::StakingInterface::current_era();
let unbond_era = T::StakingInterface::bonding_duration().saturating_add(current_era);
let current_era = T::Staking::current_era();
let unbond_era = T::Staking::bonding_duration().saturating_add(current_era);
// Unbond in the actual underlying nominator.
let unbonding_balance = bonded_pool.dissolve(unbonding_points);
T::StakingInterface::unbond(bonded_pool.bonded_account(), unbonding_balance)?;
T::Staking::unbond(&bonded_pool.bonded_account(), unbonding_balance)?;
// Note that we lazily create the unbonding pools here if they don't already exist
let mut sub_pools = SubPoolsStorage::<T>::get(member.pool_id)
@@ -1718,7 +1696,7 @@ pub mod pallet {
// For now we only allow a pool to withdraw unbonded if its not destroying. If the pool
// is destroying then `withdraw_unbonded` can be used.
ensure!(pool.state != PoolState::Destroying, Error::<T>::NotDestroying);
T::StakingInterface::withdraw_unbonded(pool.bonded_account(), num_slashing_spans)?;
T::Staking::withdraw_unbonded(pool.bonded_account(), num_slashing_spans)?;
Ok(())
}
@@ -1754,7 +1732,7 @@ pub mod pallet {
let member_account = T::Lookup::lookup(member_account)?;
let mut member =
PoolMembers::<T>::get(&member_account).ok_or(Error::<T>::PoolMemberNotFound)?;
let current_era = T::StakingInterface::current_era();
let current_era = T::Staking::current_era();
let bonded_pool = BondedPool::<T>::get(member.pool_id)
.defensive_ok_or::<Error<T>>(DefensiveError::PoolNotFound.into())?;
@@ -1769,10 +1747,8 @@ pub mod pallet {
// Before calculate the `balance_to_unbond`, with call withdraw unbonded to ensure the
// `transferrable_balance` is correct.
let stash_killed = T::StakingInterface::withdraw_unbonded(
bonded_pool.bonded_account(),
num_slashing_spans,
)?;
let stash_killed =
T::Staking::withdraw_unbonded(bonded_pool.bonded_account(), num_slashing_spans)?;
// defensive-only: the depositor puts enough funds into the stash so that it will only
// be destroyed when they are leaving.
@@ -1960,7 +1936,7 @@ pub mod pallet {
let who = ensure_signed(origin)?;
let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
T::StakingInterface::nominate(bonded_pool.bonded_account(), validators)
T::Staking::nominate(&bonded_pool.bonded_account(), validators)
}
/// Set a new state for the pool.
@@ -2132,7 +2108,7 @@ pub mod pallet {
let who = ensure_signed(origin)?;
let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
T::StakingInterface::chill(bonded_pool.bonded_account())
T::Staking::chill(&bonded_pool.bonded_account())
}
}
@@ -2149,7 +2125,7 @@ pub mod pallet {
"Minimum points to balance ratio must be greater than 0"
);
assert!(
T::StakingInterface::bonding_duration() < TotalUnbondingPools::<T>::get(),
T::Staking::bonding_duration() < TotalUnbondingPools::<T>::get(),
"There must be more unbonding pools then the bonding duration /
so a slash can be applied to relevant unboding pools. (We assume /
the bonding duration > slash deffer duration.",
@@ -2185,7 +2161,7 @@ impl<T: Config> Pallet<T> {
/// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former
/// is coming from the staking pallet and the latter two are configured in this pallet.
pub fn depositor_min_bond() -> BalanceOf<T> {
T::StakingInterface::minimum_bond()
T::Staking::minimum_nominator_bond()
.max(MinCreateBond::<T>::get())
.max(MinJoinBond::<T>::get())
.max(T::Currency::minimum_balance())
@@ -2212,7 +2188,7 @@ impl<T: Config> Pallet<T> {
debug_assert_eq!(frame_system::Pallet::<T>::consumers(&reward_account), 0);
debug_assert_eq!(frame_system::Pallet::<T>::consumers(&bonded_account), 0);
debug_assert_eq!(
T::StakingInterface::total_stake(&bonded_account).unwrap_or_default(),
T::Staking::total_stake(&bonded_account).unwrap_or_default(),
Zero::zero()
);
@@ -2487,8 +2463,7 @@ impl<T: Config> Pallet<T> {
let subs = SubPoolsStorage::<T>::get(pool_id).unwrap_or_default();
let sum_unbonding_balance = subs.sum_unbonding_balance();
let bonded_balance =
T::StakingInterface::active_stake(&pool_account).unwrap_or_default();
let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default();
let total_balance = T::Currency::total_balance(&pool_account);
assert!(
+61 -32
View File
@@ -3,6 +3,7 @@ use crate::{self as pools};
use frame_support::{assert_ok, parameter_types, PalletId};
use frame_system::RawOrigin;
use sp_runtime::FixedU128;
use sp_staking::Stake;
pub type BlockNumber = u64;
pub type AccountId = u128;
@@ -47,9 +48,16 @@ impl sp_staking::StakingInterface for StakingMock {
type Balance = Balance;
type AccountId = AccountId;
fn minimum_bond() -> Self::Balance {
fn minimum_nominator_bond() -> Self::Balance {
StakingMinBond::get()
}
fn minimum_validator_bond() -> Self::Balance {
StakingMinBond::get()
}
fn desired_validator_count() -> u32 {
unimplemented!("method currently not used in testing")
}
fn current_era() -> EraIndex {
CurrentEra::get()
@@ -59,39 +67,24 @@ impl sp_staking::StakingInterface for StakingMock {
BondingDuration::get()
}
fn active_stake(who: &Self::AccountId) -> Option<Self::Balance> {
BondedBalanceMap::get().get(who).map(|v| *v)
}
fn total_stake(who: &Self::AccountId) -> Option<Self::Balance> {
match (
UnbondingBalanceMap::get().get(who).map(|v| *v),
BondedBalanceMap::get().get(who).map(|v| *v),
) {
(None, None) => None,
(Some(v), None) | (None, Some(v)) => Some(v),
(Some(a), Some(b)) => Some(a + b),
}
}
fn bond_extra(who: Self::AccountId, extra: Self::Balance) -> DispatchResult {
fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
let mut x = BondedBalanceMap::get();
x.get_mut(&who).map(|v| *v += extra);
x.get_mut(who).map(|v| *v += extra);
BondedBalanceMap::set(&x);
Ok(())
}
fn unbond(who: Self::AccountId, amount: Self::Balance) -> DispatchResult {
fn unbond(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult {
let mut x = BondedBalanceMap::get();
*x.get_mut(&who).unwrap() = x.get_mut(&who).unwrap().saturating_sub(amount);
*x.get_mut(who).unwrap() = x.get_mut(who).unwrap().saturating_sub(amount);
BondedBalanceMap::set(&x);
let mut y = UnbondingBalanceMap::get();
*y.entry(who).or_insert(Self::Balance::zero()) += amount;
*y.entry(*who).or_insert(Self::Balance::zero()) += amount;
UnbondingBalanceMap::set(&y);
Ok(())
}
fn chill(_: Self::AccountId) -> sp_runtime::DispatchResult {
fn chill(_: &Self::AccountId) -> sp_runtime::DispatchResult {
Ok(())
}
@@ -104,17 +97,12 @@ impl sp_staking::StakingInterface for StakingMock {
Ok(UnbondingBalanceMap::get().is_empty() && BondedBalanceMap::get().is_empty())
}
fn bond(
stash: Self::AccountId,
_: Self::AccountId,
value: Self::Balance,
_: Self::AccountId,
) -> DispatchResult {
StakingMock::set_bonded_balance(stash, value);
fn bond(stash: &Self::AccountId, value: Self::Balance, _: &Self::AccountId) -> DispatchResult {
StakingMock::set_bonded_balance(*stash, value);
Ok(())
}
fn nominate(_: Self::AccountId, nominations: Vec<Self::AccountId>) -> DispatchResult {
fn nominate(_: &Self::AccountId, nominations: Vec<Self::AccountId>) -> DispatchResult {
Nominations::set(&Some(nominations));
Ok(())
}
@@ -123,6 +111,48 @@ impl sp_staking::StakingInterface for StakingMock {
fn nominations(_: Self::AccountId) -> Option<Vec<Self::AccountId>> {
Nominations::get()
}
fn stash_by_ctrl(_controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
unimplemented!("method currently not used in testing")
}
fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError> {
match (
UnbondingBalanceMap::get().get(who).map(|v| *v),
BondedBalanceMap::get().get(who).map(|v| *v),
) {
(None, None) => Err(DispatchError::Other("balance not found")),
(Some(v), None) => Ok(Stake { total: v, active: 0, stash: *who }),
(None, Some(v)) => Ok(Stake { total: v, active: v, stash: *who }),
(Some(a), Some(b)) => Ok(Stake { total: a + b, active: b, stash: *who }),
}
}
fn election_ongoing() -> bool {
unimplemented!("method currently not used in testing")
}
fn force_unstake(_who: Self::AccountId) -> sp_runtime::DispatchResult {
unimplemented!("method currently not used in testing")
}
fn is_exposed_in_era(_who: &Self::AccountId, _era: &EraIndex) -> bool {
unimplemented!("method currently not used in testing")
}
#[cfg(feature = "runtime-benchmarks")]
fn add_era_stakers(
_current_era: &EraIndex,
_stash: &Self::AccountId,
_exposures: Vec<(Self::AccountId, Self::Balance)>,
) {
unimplemented!("method currently not used in testing")
}
#[cfg(feature = "runtime-benchmarks")]
fn set_current_era(_era: EraIndex) {
unimplemented!("method currently not used in testing")
}
}
impl frame_system::Config for Runtime {
@@ -192,11 +222,10 @@ impl pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = RewardCounter;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = StakingMock;
type Staking = StakingMock;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type PalletId = PoolsPalletId;
type MaxMetadataLen = MaxMetadataLen;
+16 -13
View File
@@ -3083,18 +3083,18 @@ mod pool_withdraw_unbonded {
fn pool_withdraw_unbonded_works() {
ExtBuilder::default().build_and_execute(|| {
// Given 10 unbond'ed directly against the pool account
assert_ok!(StakingMock::unbond(default_bonded_account(), 5));
assert_ok!(StakingMock::unbond(&default_bonded_account(), 5));
// and the pool account only has 10 balance
assert_eq!(StakingMock::active_stake(&default_bonded_account()), Some(5));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Some(10));
assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(10));
assert_eq!(Balances::free_balance(&default_bonded_account()), 10);
// When
assert_ok!(Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0));
// Then there unbonding balance is no longer locked
assert_eq!(StakingMock::active_stake(&default_bonded_account()), Some(5));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Some(5));
assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(5));
assert_eq!(Balances::free_balance(&default_bonded_account()), 10);
});
}
@@ -3270,7 +3270,7 @@ mod withdraw_unbonded {
// current bond is 600, we slash it all to 300.
StakingMock::set_bonded_balance(default_bonded_account(), 300);
Balances::make_free_balance_be(&default_bonded_account(), 300);
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Some(300));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(300));
assert_ok!(fully_unbond_permissioned(40));
assert_ok!(fully_unbond_permissioned(550));
@@ -4074,12 +4074,15 @@ mod create {
assert!(!BondedPools::<Runtime>::contains_key(2));
assert!(!RewardPools::<Runtime>::contains_key(2));
assert!(!PoolMembers::<Runtime>::contains_key(11));
assert_eq!(StakingMock::active_stake(&next_pool_stash), None);
assert_err!(
StakingMock::active_stake(&next_pool_stash),
DispatchError::Other("balance not found")
);
Balances::make_free_balance_be(&11, StakingMock::minimum_bond() + ed);
Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed);
assert_ok!(Pools::create(
RuntimeOrigin::signed(11),
StakingMock::minimum_bond(),
StakingMock::minimum_nominator_bond(),
123,
456,
789
@@ -4090,7 +4093,7 @@ mod create {
PoolMembers::<Runtime>::get(11).unwrap(),
PoolMember {
pool_id: 2,
points: StakingMock::minimum_bond(),
points: StakingMock::minimum_nominator_bond(),
..Default::default()
}
);
@@ -4099,7 +4102,7 @@ mod create {
BondedPool {
id: 2,
inner: BondedPoolInner {
points: StakingMock::minimum_bond(),
points: StakingMock::minimum_nominator_bond(),
member_counter: 1,
state: PoolState::Open,
roles: PoolRoles {
@@ -4113,7 +4116,7 @@ mod create {
);
assert_eq!(
StakingMock::active_stake(&next_pool_stash).unwrap(),
StakingMock::minimum_bond()
StakingMock::minimum_nominator_bond()
);
assert_eq!(
RewardPools::<Runtime>::get(2).unwrap(),
@@ -4142,7 +4145,7 @@ mod create {
// Given
assert_eq!(MinCreateBond::<Runtime>::get(), 2);
assert_eq!(StakingMock::minimum_bond(), 10);
assert_eq!(StakingMock::minimum_nominator_bond(), 10);
// Then
assert_noop!(
@@ -171,11 +171,10 @@ impl pallet_nomination_pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = Staking;
type Staking = Staking;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>;
+80 -27
View File
@@ -18,8 +18,8 @@
//! Implementations for the Staking FRAME Pallet.
use frame_election_provider_support::{
data_provider, ElectionDataProvider, ElectionProvider, ScoreProvider, SortedListProvider,
Supports, VoteWeight, VoterOf,
data_provider, ElectionDataProvider, ElectionProvider, ElectionProviderBase, ScoreProvider,
SortedListProvider, Supports, VoteWeight, VoterOf,
};
use frame_support::{
dispatch::WithPostDispatchInfo,
@@ -38,7 +38,7 @@ use sp_runtime::{
};
use sp_staking::{
offence::{DisableStrategy, OffenceDetails, OnOffenceHandler},
EraIndex, SessionIndex, StakingInterface,
EraIndex, SessionIndex, Stake, StakingInterface,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
@@ -1482,14 +1482,44 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsM
}
}
// NOTE: in this entire impl block, the assumption is that `who` is a stash account.
impl<T: Config> StakingInterface for Pallet<T> {
type AccountId = T::AccountId;
type Balance = BalanceOf<T>;
fn minimum_bond() -> Self::Balance {
fn minimum_nominator_bond() -> Self::Balance {
MinNominatorBond::<T>::get()
}
fn minimum_validator_bond() -> Self::Balance {
MinValidatorBond::<T>::get()
}
fn desired_validator_count() -> u32 {
ValidatorCount::<T>::get()
}
fn election_ongoing() -> bool {
<T::ElectionProvider as ElectionProviderBase>::ongoing()
}
fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult {
let num_slashing_spans = Self::slashing_spans(&who).iter().count() as u32;
Self::force_unstake(RawOrigin::Root.into(), who.clone(), num_slashing_spans)
}
fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
Self::ledger(controller)
.map(|l| l.stash)
.ok_or(Error::<T>::NotController.into())
}
fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool {
ErasStakers::<T>::iter_prefix(era).any(|(validator, exposures)| {
validator == *who || exposures.others.iter().any(|i| i.who == *who)
})
}
fn bonding_duration() -> EraIndex {
T::BondingDuration::get()
}
@@ -1498,58 +1528,81 @@ impl<T: Config> StakingInterface for Pallet<T> {
Self::current_era().unwrap_or(Zero::zero())
}
fn active_stake(controller: &Self::AccountId) -> Option<Self::Balance> {
Self::ledger(controller).map(|l| l.active)
fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError> {
Self::bonded(who)
.and_then(|c| Self::ledger(c))
.map(|l| Stake { stash: l.stash, total: l.total, active: l.active })
.ok_or(Error::<T>::NotStash.into())
}
fn total_stake(controller: &Self::AccountId) -> Option<Self::Balance> {
Self::ledger(controller).map(|l| l.total)
fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra)
}
fn bond_extra(stash: Self::AccountId, extra: Self::Balance) -> DispatchResult {
Self::bond_extra(RawOrigin::Signed(stash).into(), extra)
fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::unbond(RawOrigin::Signed(ctrl).into(), value)
}
fn unbond(controller: Self::AccountId, value: Self::Balance) -> DispatchResult {
Self::unbond(RawOrigin::Signed(controller).into(), value)
}
fn chill(controller: Self::AccountId) -> DispatchResult {
Self::chill(RawOrigin::Signed(controller).into())
fn chill(who: &Self::AccountId) -> DispatchResult {
// defensive-only: any account bonded via this interface has the stash set as the
// controller, but we have to be sure. Same comment anywhere else that we read this.
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::chill(RawOrigin::Signed(ctrl).into())
}
fn withdraw_unbonded(
controller: Self::AccountId,
who: Self::AccountId,
num_slashing_spans: u32,
) -> Result<bool, DispatchError> {
Self::withdraw_unbonded(RawOrigin::Signed(controller.clone()).into(), num_slashing_spans)
.map(|_| !Ledger::<T>::contains_key(&controller))
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans)
.map(|_| !Ledger::<T>::contains_key(&ctrl))
.map_err(|with_post| with_post.error)
}
fn bond(
stash: Self::AccountId,
controller: Self::AccountId,
who: &Self::AccountId,
value: Self::Balance,
payee: Self::AccountId,
payee: &Self::AccountId,
) -> DispatchResult {
Self::bond(
RawOrigin::Signed(stash).into(),
T::Lookup::unlookup(controller),
RawOrigin::Signed(who.clone()).into(),
T::Lookup::unlookup(who.clone()),
value,
RewardDestination::Account(payee),
RewardDestination::Account(payee.clone()),
)
}
fn nominate(controller: Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
fn nominate(who: &Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
let targets = targets.into_iter().map(T::Lookup::unlookup).collect::<Vec<_>>();
Self::nominate(RawOrigin::Signed(controller).into(), targets)
Self::nominate(RawOrigin::Signed(ctrl).into(), targets)
}
#[cfg(feature = "runtime-benchmarks")]
fn nominations(who: Self::AccountId) -> Option<Vec<T::AccountId>> {
Nominators::<T>::get(who).map(|n| n.targets.into_inner())
}
#[cfg(feature = "runtime-benchmarks")]
fn add_era_stakers(
current_era: &EraIndex,
stash: &T::AccountId,
exposures: Vec<(Self::AccountId, Self::Balance)>,
) {
let others = exposures
.iter()
.map(|(who, value)| IndividualExposure { who: who.clone(), value: value.clone() })
.collect::<Vec<_>>();
let exposure = Exposure { total: Default::default(), own: Default::default(), others };
Self::add_era_stakers(current_era.clone(), stash.clone(), exposure)
}
#[cfg(feature = "runtime-benchmarks")]
fn set_current_era(era: EraIndex) {
CurrentEra::<T>::put(era);
}
}
#[cfg(any(test, feature = "try-runtime"))]
+1 -1
View File
@@ -262,7 +262,7 @@ pub mod pallet {
type WeightInfo: WeightInfo;
}
/// The ideal number of staking participants.
/// The ideal number of active validators.
#[pallet::storage]
#[pallet::getter(fn validator_count)]
pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
+94 -43
View File
@@ -19,8 +19,9 @@
//! A crate which contains primitives that are useful for implementation that uses staking
//! approaches in general. Definitions related to sessions, slashing, etc go here.
use sp_runtime::{DispatchError, DispatchResult};
use sp_std::collections::btree_map::BTreeMap;
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
pub mod offence;
@@ -54,25 +55,55 @@ impl<AccountId, Balance> OnStakerSlash<AccountId, Balance> for () {
}
}
/// Trait for communication with the staking pallet.
/// A struct that reflects stake that an account has in the staking system. Provides a set of
/// methods to operate on it's properties. Aimed at making `StakingInterface` more concise.
pub struct Stake<T: StakingInterface + ?Sized> {
/// The stash account whose balance is actually locked and at stake.
pub stash: T::AccountId,
/// The total stake that `stash` has in the staking system. This includes the
/// `active` stake, and any funds currently in the process of unbonding via
/// [`StakingInterface::unbond`].
///
/// # Note
///
/// This is only guaranteed to reflect the amount locked by the staking system. If there are
/// non-staking locks on the bonded pair's balance this amount is going to be larger in
/// reality.
pub total: T::Balance,
/// The total amount of the stash's balance that will be at stake in any forthcoming
/// rounds.
pub active: T::Balance,
}
/// A generic representation of a staking implementation.
///
/// This interface uses the terminology of NPoS, but it is aims to be generic enough to cover other
/// implementations as well.
pub trait StakingInterface {
/// Balance type used by the staking system.
type Balance;
type Balance: PartialEq;
/// AccountId type used by the staking system
type AccountId;
/// The minimum amount required to bond in order to be a nominator. This does not necessarily
/// mean the nomination will be counted in an election, but instead just enough to be stored as
/// a nominator. In other words, this is the minimum amount to register the intention to
/// nominate.
fn minimum_bond() -> Self::Balance;
/// The minimum amount required to bond in order to set nomination intentions. This does not
/// necessarily mean the nomination will be counted in an election, but instead just enough to
/// be stored as a nominator. In other words, this is the minimum amount to register the
/// intention to nominate.
fn minimum_nominator_bond() -> Self::Balance;
/// The minimum amount required to bond in order to set validation intentions.
fn minimum_validator_bond() -> Self::Balance;
/// Return a stash account that is controlled by a `controller`.
///
/// ## Note
///
/// The controller abstraction is not permanent and might go away. Avoid using this as much as
/// possible.
fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError>;
/// Number of eras that staked funds must remain bonded for.
///
/// # Note
///
/// This must be strictly greater than the staking systems slash deffer duration.
fn bonding_duration() -> EraIndex;
/// The current era index.
@@ -80,41 +111,39 @@ pub trait StakingInterface {
/// This should be the latest planned era that the staking system knows about.
fn current_era() -> EraIndex;
/// The amount of active stake that `stash` has in the staking system.
fn active_stake(stash: &Self::AccountId) -> Option<Self::Balance>;
/// Returns the stake of `who`.
fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError>;
/// The total stake that `stash` has in the staking system. This includes the
/// [`Self::active_stake`], and any funds currently in the process of unbonding via
/// [`Self::unbond`].
///
/// # Note
///
/// This is only guaranteed to reflect the amount locked by the staking system. If there are
/// non-staking locks on the bonded pair's balance this may not be accurate.
fn total_stake(stash: &Self::AccountId) -> Option<Self::Balance>;
fn total_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
Self::stake(who).map(|s| s.total)
}
/// Bond (lock) `value` of `stash`'s balance. `controller` will be set as the account
/// controlling `stash`. This creates what is referred to as "bonded pair".
fn bond(
stash: Self::AccountId,
controller: Self::AccountId,
value: Self::Balance,
payee: Self::AccountId,
) -> DispatchResult;
fn active_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
Self::stake(who).map(|s| s.active)
}
/// Have `controller` nominate `validators`.
fn nominate(
controller: Self::AccountId,
validators: sp_std::vec::Vec<Self::AccountId>,
) -> DispatchResult;
fn is_unbonding(who: &Self::AccountId) -> Result<bool, DispatchError> {
Self::stake(who).map(|s| s.active != s.total)
}
/// Chill `stash`.
fn chill(controller: Self::AccountId) -> DispatchResult;
fn fully_unbond(who: &Self::AccountId) -> DispatchResult {
Self::unbond(who, Self::stake(who)?.active)
}
/// Bond some extra amount in the _Stash_'s free balance against the active bonded balance of
/// the account. The amount extra actually bonded will never be more than the _Stash_'s free
/// Bond (lock) `value` of `who`'s balance, while forwarding any rewards to `payee`.
fn bond(who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId)
-> DispatchResult;
/// Have `who` nominate `validators`.
fn nominate(who: &Self::AccountId, validators: Vec<Self::AccountId>) -> DispatchResult;
/// Chill `who`.
fn chill(who: &Self::AccountId) -> DispatchResult;
/// Bond some extra amount in `who`'s free balance against the active bonded balance of
/// the account. The amount extra actually bonded will never be more than `who`'s free
/// balance.
fn bond_extra(stash: Self::AccountId, extra: Self::Balance) -> DispatchResult;
fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult;
/// Schedule a portion of the active bonded balance to be unlocked at era
/// [Self::current_era] + [`Self::bonding_duration`].
@@ -125,7 +154,7 @@ pub trait StakingInterface {
/// The amount of times this can be successfully called is limited based on how many distinct
/// eras funds are schedule to unlock in. Calling [`Self::withdraw_unbonded`] after some unlock
/// schedules have reached their unlocking era should allow more calls to this function.
fn unbond(stash: Self::AccountId, value: Self::Balance) -> DispatchResult;
fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult;
/// Unlock any funds schedule to unlock before or at the current era.
///
@@ -135,7 +164,29 @@ pub trait StakingInterface {
num_slashing_spans: u32,
) -> Result<bool, DispatchError>;
/// The ideal number of active validators.
fn desired_validator_count() -> u32;
/// Whether or not there is an ongoing election.
fn election_ongoing() -> bool;
/// Force a current staker to become completely unstaked, immediately.
fn force_unstake(who: Self::AccountId) -> DispatchResult;
/// Checks whether an account `staker` has been exposed in an era.
fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool;
/// Get the nominations of a stash, if they are a nominator, `None` otherwise.
#[cfg(feature = "runtime-benchmarks")]
fn nominations(who: Self::AccountId) -> Option<sp_std::prelude::Vec<Self::AccountId>>;
fn nominations(who: Self::AccountId) -> Option<Vec<Self::AccountId>>;
#[cfg(feature = "runtime-benchmarks")]
fn add_era_stakers(
current_era: &EraIndex,
stash: &Self::AccountId,
exposures: Vec<(Self::AccountId, Self::Balance)>,
);
#[cfg(feature = "runtime-benchmarks")]
fn set_current_era(era: EraIndex);
}