[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 RuntimeEvent = RuntimeEvent;
type ControlOrigin = frame_system::EnsureRoot<AccountId>; type ControlOrigin = frame_system::EnsureRoot<AccountId>;
type Deposit = ConstU128<{ DOLLARS }>; type Deposit = ConstU128<{ DOLLARS }>;
type DepositCurrency = Balances; type Currency = Balances;
type Staking = Staking;
type WeightInfo = (); type WeightInfo = ();
} }
@@ -773,11 +774,10 @@ impl pallet_nomination_pools::Config for Runtime {
type WeightInfo = (); type WeightInfo = ();
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type Currency = Balances; type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128; type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256; type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance; type U256ToBalance = U256ToBalance;
type StakingInterface = pallet_staking::Pallet<Self>; type Staking = Staking;
type PostUnbondingPoolsWindow = PostUnbondPoolsWindow; type PostUnbondingPoolsWindow = PostUnbondPoolsWindow;
type MaxMetadataLen = ConstU32<256>; type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>; 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-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" }
sp-staking = { default-features = false, path = "../../primitives/staking" } 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-election-provider-support = { default-features = false, path = "../election-provider-support" }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } 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" } sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" }
substrate-test-utils = { version = "4.0.0-dev", path = "../../test-utils" } substrate-test-utils = { version = "4.0.0-dev", path = "../../test-utils" }
sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" } sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" }
pallet-staking = { path = "../staking" }
pallet-balances = { path = "../balances" }
pallet-timestamp = { path = "../timestamp" }
[features] [features]
default = ["std"] default = ["std"]
@@ -53,9 +53,6 @@ std = [
"sp-runtime/std", "sp-runtime/std",
"sp-std/std", "sp-std/std",
"pallet-staking/std",
"pallet-balances/std",
"pallet-timestamp/std",
"frame-election-provider-support/std", "frame-election-provider-support/std",
"frame-benchmarking/std", "frame-benchmarking/std",
@@ -63,6 +60,6 @@ std = [
runtime-benchmarks = [ runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks", "frame-system/runtime-benchmarks",
"pallet-staking/runtime-benchmarks", "sp-staking/runtime-benchmarks"
] ]
try-runtime = ["frame-support/try-runtime"] try-runtime = ["frame-support/try-runtime"]
@@ -26,22 +26,15 @@ use frame_support::{
traits::{Currency, EnsureOrigin, Get, Hooks}, traits::{Currency, EnsureOrigin, Get, Hooks},
}; };
use frame_system::RawOrigin; use frame_system::RawOrigin;
use pallet_staking::Pallet as Staking; use sp_runtime::traits::Zero;
use sp_runtime::traits::{StaticLookup, Zero}; use sp_staking::{EraIndex, StakingInterface};
use sp_staking::EraIndex;
use sp_std::prelude::*; use sp_std::prelude::*;
const USER_SEED: u32 = 0; const USER_SEED: u32 = 0;
const DEFAULT_BACKER_PER_VALIDATOR: u32 = 128; const DEFAULT_BACKER_PER_VALIDATOR: u32 = 128;
const MAX_VALIDATORS: u32 = 128; const MAX_VALIDATORS: u32 = 128;
type CurrencyOf<T> = <T as pallet_staking::Config>::Currency; type CurrencyOf<T> = <T as Config>::Currency;
fn l<T: Config>(
who: T::AccountId,
) -> <<T as frame_system::Config>::Lookup as StaticLookup>::Source {
T::Lookup::unlookup(who)
}
fn create_unexposed_nominator<T: Config>() -> T::AccountId { fn create_unexposed_nominator<T: Config>() -> T::AccountId {
let account = frame_benchmarking::account::<T::AccountId>("nominator_42", 0, USER_SEED); 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(); let stake = CurrencyOf::<T>::minimum_balance() * 100u32.into();
CurrencyOf::<T>::make_free_balance_be(&account, stake * 10u32.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. // bond and nominate ourselves, this will guarantee that we are not backing anyone.
assert_ok!(Staking::<T>::bond( assert_ok!(T::Staking::bond(account, stake, account));
RawOrigin::Signed(account.clone()).into(), assert_ok!(T::Staking::nominate(account, vec![account.clone()]));
account_lookup.clone(),
stake,
pallet_staking::RewardDestination::Controller,
));
assert_ok!(Staking::<T>::nominate(
RawOrigin::Signed(account.clone()).into(),
vec![account_lookup]
));
} }
pub(crate) fn fast_unstake_events<T: Config>() -> Vec<crate::Event<T>> { 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| { .map(|s| {
let who = frame_benchmarking::account::<T::AccountId>("nominator", era, s); let who = frame_benchmarking::account::<T::AccountId>("nominator", era, s);
let value = ed; let value = ed;
pallet_staking::IndividualExposure { who, value } (who, value)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let exposure =
pallet_staking::Exposure { total: Default::default(), own: Default::default(), others };
validators.iter().for_each(|v| { 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, when we check some number of eras,
on_idle_check { on_idle_check {
// number of eras multiplied by validators in that era. // 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::Staking::bonding_duration();
let u = <T as pallet_staking::Config>::BondingDuration::get(); let v = x / u;
ErasToCheckPerBlock::<T>::put(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 with v validators and u eras of data (0..=u)
setup_staking::<T>(v, u); setup_staking::<T>(v, u);
+34 -51
View File
@@ -80,18 +80,16 @@ macro_rules! log {
pub mod pallet { pub mod pallet {
use super::*; use super::*;
use crate::types::*; use crate::types::*;
use frame_election_provider_support::ElectionProviderBase;
use frame_support::{ use frame_support::{
pallet_prelude::*, pallet_prelude::*,
traits::{Defensive, ReservableCurrency}, traits::{Defensive, ReservableCurrency},
}; };
use frame_system::{pallet_prelude::*, RawOrigin}; use frame_system::pallet_prelude::*;
use pallet_staking::Pallet as Staking;
use sp_runtime::{ use sp_runtime::{
traits::{Saturating, Zero}, traits::{Saturating, Zero},
DispatchResult, DispatchResult,
}; };
use sp_staking::EraIndex; use sp_staking::{EraIndex, StakingInterface};
use sp_std::{prelude::*, vec::Vec}; use sp_std::{prelude::*, vec::Vec};
pub use weights::WeightInfo; pub use weights::WeightInfo;
@@ -101,7 +99,7 @@ pub mod pallet {
pub struct MaxChecking<T: Config>(sp_std::marker::PhantomData<T>); pub struct MaxChecking<T: Config>(sp_std::marker::PhantomData<T>);
impl<T: Config> frame_support::traits::Get<u32> for MaxChecking<T> { impl<T: Config> frame_support::traits::Get<u32> for MaxChecking<T> {
fn get() -> u32 { 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>(_); pub struct Pallet<T>(_);
#[pallet::config] #[pallet::config]
pub trait Config: frame_system::Config + pallet_staking::Config { pub trait Config: frame_system::Config {
/// The overarching event type. /// The overarching event type.
type RuntimeEvent: From<Event<Self>> type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent> + IsType<<Self as frame_system::Config>::RuntimeEvent>
+ TryInto<Event<Self>>; + TryInto<Event<Self>>;
/// The currency used for deposits. /// 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 /// 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. /// the costs of resources on unsuccessful unstake.
@@ -125,6 +123,9 @@ pub mod pallet {
/// The origin that can control this pallet. /// The origin that can control this pallet.
type ControlOrigin: frame_support::traits::EnsureOrigin<Self::RuntimeOrigin>; 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. /// The weight information of this pallet.
type WeightInfo: WeightInfo; type WeightInfo: WeightInfo;
} }
@@ -221,28 +222,25 @@ pub mod pallet {
let ctrl = ensure_signed(origin)?; let ctrl = ensure_signed(origin)?;
ensure!(ErasToCheckPerBlock::<T>::get() != 0, <Error<T>>::CallNotAllowed); ensure!(ErasToCheckPerBlock::<T>::get() != 0, <Error<T>>::CallNotAllowed);
let stash_account =
let ledger = T::Staking::stash_by_ctrl(&ctrl).map_err(|_| Error::<T>::NotController)?;
pallet_staking::Ledger::<T>::get(&ctrl).ok_or(Error::<T>::NotController)?; ensure!(!Queue::<T>::contains_key(&stash_account), Error::<T>::AlreadyQueued);
ensure!(!Queue::<T>::contains_key(&ledger.stash), Error::<T>::AlreadyQueued);
ensure!( 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 Error::<T>::AlreadyHead
); );
// second part of the && is defensive.
ensure!( ensure!(!T::Staking::is_unbonding(&stash_account)?, Error::<T>::NotFullyBonded);
ledger.active == ledger.total && ledger.unlocking.is_empty(),
Error::<T>::NotFullyBonded
);
// chill and fully unstake. // chill and fully unstake.
Staking::<T>::chill(RawOrigin::Signed(ctrl.clone()).into())?; T::Staking::chill(&stash_account)?;
Staking::<T>::unbond(RawOrigin::Signed(ctrl).into(), ledger.total)?; T::Staking::fully_unbond(&stash_account)?;
T::DepositCurrency::reserve(&ledger.stash, T::Deposit::get())?; T::Currency::reserve(&stash_account, T::Deposit::get())?;
// enqueue them. // enqueue them.
Queue::<T>::insert(ledger.stash, T::Deposit::get()); Queue::<T>::insert(stash_account, T::Deposit::get());
Ok(()) Ok(())
} }
@@ -259,18 +257,18 @@ pub mod pallet {
ensure!(ErasToCheckPerBlock::<T>::get() != 0, <Error<T>>::CallNotAllowed); ensure!(ErasToCheckPerBlock::<T>::get() != 0, <Error<T>>::CallNotAllowed);
let stash = pallet_staking::Ledger::<T>::get(&ctrl) let stash_account =
.map(|l| l.stash) T::Staking::stash_by_ctrl(&ctrl).map_err(|_| Error::<T>::NotController)?;
.ok_or(Error::<T>::NotController)?; ensure!(Queue::<T>::contains_key(&stash_account), Error::<T>::NotQueued);
ensure!(Queue::<T>::contains_key(&stash), Error::<T>::NotQueued);
ensure!( 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 Error::<T>::AlreadyHead
); );
let deposit = Queue::<T>::take(stash.clone()); let deposit = Queue::<T>::take(stash_account.clone());
if let Some(deposit) = deposit.defensive() { 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() { if !remaining.is_zero() {
frame_support::defensive!("`not enough balance to unreserve`"); frame_support::defensive!("`not enough balance to unreserve`");
ErasToCheckPerBlock::<T>::put(0); 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, // 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. // 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` // determine the number of eras to check. This is based on both `ErasToCheckPerBlock`
// and `remaining_weight` passed on to us from the runtime executive. // 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. // NOTE: we assume `ongoing` does not consume any weight.
// there is an ongoing election -- we better not do anything. Imagine someone is not // 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 // 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. // the range that we're allowed to check in this round.
let current_era = pallet_staking::CurrentEra::<T>::get().unwrap_or_default(); let current_era = T::Staking::current_era();
let bonding_duration = <T as pallet_staking::Config>::BondingDuration::get(); 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 // prune all the old eras that we don't care about. This will help us keep the bound
// of `checked`. // of `checked`.
checked.retain(|e| *e >= current_era.saturating_sub(bonding_duration)); checked.retain(|e| *e >= current_era.saturating_sub(bonding_duration));
@@ -401,16 +398,9 @@ pub mod pallet {
); );
if unchecked_eras_to_check.is_empty() { if unchecked_eras_to_check.is_empty() {
// `stash` is not exposed in any era now -- we can let go of them now. let result = T::Staking::force_unstake(stash.clone());
let num_slashing_spans = Staking::<T>::slashing_spans(&stash).iter().count() as u32;
let result = pallet_staking::Pallet::<T>::force_unstake( let remaining = T::Currency::unreserve(&stash, deposit);
RawOrigin::Root.into(),
stash.clone(),
num_slashing_spans,
);
let remaining = T::DepositCurrency::unreserve(&stash, deposit);
if !remaining.is_zero() { if !remaining.is_zero() {
frame_support::defensive!("`not enough balance to unreserve`"); frame_support::defensive!("`not enough balance to unreserve`");
ErasToCheckPerBlock::<T>::put(0); ErasToCheckPerBlock::<T>::put(0);
@@ -426,7 +416,7 @@ pub mod pallet {
let mut eras_checked = 0u32; let mut eras_checked = 0u32;
let is_exposed = unchecked_eras_to_check.iter().any(|e| { let is_exposed = unchecked_eras_to_check.iter().any(|e| {
eras_checked.saturating_inc(); eras_checked.saturating_inc();
Self::is_exposed_in_era(&stash, e) T::Staking::is_exposed_in_era(&stash, e)
}); });
log!( log!(
@@ -442,7 +432,7 @@ pub mod pallet {
// the last 28 eras, have registered yourself to be unstaked, midway being checked, // the last 28 eras, have registered yourself to be unstaked, midway being checked,
// you are exposed. // you are exposed.
if is_exposed { if is_exposed {
T::DepositCurrency::slash_reserved(&stash, deposit); T::Currency::slash_reserved(&stash, deposit);
log!(info, "slashed {:?} by {:?}", stash, deposit); log!(info, "slashed {:?} by {:?}", stash, deposit);
Self::deposit_event(Event::<T>::Slashed { stash, amount: deposit }); Self::deposit_event(Event::<T>::Slashed { stash, amount: deposit });
} else { } else {
@@ -472,12 +462,5 @@ pub mod pallet {
<T as Config>::WeightInfo::on_idle_check(validator_count * eras_checked) <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 { impl fast_unstake::Config for Runtime {
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type Deposit = DepositAmount; type Deposit = DepositAmount;
type DepositCurrency = Balances; type Currency = Balances;
type Staking = Staking;
type ControlOrigin = frame_system::EnsureRoot<Self::AccountId>; type ControlOrigin = frame_system::EnsureRoot<Self::AccountId>;
type WeightInfo = (); type WeightInfo = ();
} }
+7 -7
View File
@@ -48,7 +48,7 @@ fn register_insufficient_funds_fails() {
use pallet_balances::Error as BalancesError; use pallet_balances::Error as BalancesError;
ExtBuilder::default().build_and_execute(|| { ExtBuilder::default().build_and_execute(|| {
ErasToCheckPerBlock::<T>::put(1); 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. // Controller account registers for fast unstake.
assert_noop!( assert_noop!(
@@ -138,15 +138,15 @@ fn deregister_works() {
ExtBuilder::default().build_and_execute(|| { ExtBuilder::default().build_and_execute(|| {
ErasToCheckPerBlock::<T>::put(1); 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. // Controller account registers for fast unstake.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2))); 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. // Controller then changes mind and deregisters.
assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(2))); 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. // Ensure stash no longer exists in the queue.
assert_eq!(Queue::<T>::get(1), None); assert_eq!(Queue::<T>::get(1), None);
@@ -363,7 +363,7 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get()); CurrentEra::<T>::put(BondingDuration::get());
// given // 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(2)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4))); 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(8)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(10))); 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!(Queue::<T>::count(), 5);
assert_eq!(Head::<T>::get(), None); assert_eq!(Head::<T>::get(), None);
@@ -411,7 +411,7 @@ mod on_idle {
); );
assert_eq!(Queue::<T>::count(), 3); 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!( assert_eq!(
fast_unstake_events_since_last_call(), fast_unstake_events_since_last_call(),
+3 -4
View File
@@ -17,6 +17,7 @@
//! Types used in the Fast Unstake pallet. //! Types used in the Fast Unstake pallet.
use crate::Config;
use codec::{Decode, Encode, MaxEncodedLen}; use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{ use frame_support::{
traits::{Currency, Get}, traits::{Currency, Get},
@@ -26,10 +27,8 @@ use scale_info::TypeInfo;
use sp_staking::EraIndex; use sp_staking::EraIndex;
use sp_std::{fmt::Debug, prelude::*}; use sp_std::{fmt::Debug, prelude::*};
pub type BalanceOf<T> = <<T as pallet_staking::Config>::Currency as Currency< pub type BalanceOf<T> =
<T as frame_system::Config>::AccountId, <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
>>::Balance;
/// An unstake request. /// An unstake request.
#[derive( #[derive(
Encode, Decode, EqNoBound, PartialEqNoBound, Clone, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen, Encode, Decode, EqNoBound, PartialEqNoBound, Clone, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen,
@@ -133,15 +133,15 @@ impl<T: Config> ListScenario<T> {
// Create accounts with the origin weight // Create accounts with the origin weight
let (pool_creator1, pool_origin1) = create_pool_account::<T>(USER_SEED + 1, origin_weight); let (pool_creator1, pool_origin1) = create_pool_account::<T>(USER_SEED + 1, origin_weight);
T::StakingInterface::nominate( T::Staking::nominate(
pool_origin1.clone(), &pool_origin1,
// NOTE: these don't really need to be validators. // NOTE: these don't really need to be validators.
vec![account("random_validator", 0, USER_SEED)], vec![account("random_validator", 0, USER_SEED)],
)?; )?;
let (_, pool_origin2) = create_pool_account::<T>(USER_SEED + 2, origin_weight); let (_, pool_origin2) = create_pool_account::<T>(USER_SEED + 2, origin_weight);
T::StakingInterface::nominate( T::Staking::nominate(
pool_origin2.clone(), &pool_origin2,
vec![account("random_validator", 0, USER_SEED)].clone(), 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 // Create an account with the worst case destination weight
let (_, pool_dest1) = create_pool_account::<T>(USER_SEED + 3, dest_weight); let (_, pool_dest1) = create_pool_account::<T>(USER_SEED + 3, dest_weight);
T::StakingInterface::nominate( T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?;
pool_dest1.clone(),
vec![account("random_validator", 0, USER_SEED)],
)?;
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn(); let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin1)).unwrap(), origin_weight); 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()); self.origin1_member = Some(joiner.clone());
CurrencyOf::<T>::make_free_balance_be(&joiner, amount * 2u32.into()); 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 // Unbond `amount` from the underlying pool account so when the member joins
// we will maintain `current_bonded`. // we will maintain `current_bonded`.
T::StakingInterface::unbond(self.origin1.clone(), amount) T::Staking::unbond(&self.origin1, amount).expect("the pool was created in `Self::new`.");
.expect("the pool was created in `Self::new`.");
// Account pool points for the unbonded balance. // Account pool points for the unbonded balance.
BondedPools::<T>::mutate(&1, |maybe_pool| { BondedPools::<T>::mutate(&1, |maybe_pool| {
@@ -219,7 +215,7 @@ frame_benchmarking::benchmarks! {
// setup the worst case list scenario. // setup the worst case list scenario.
let scenario = ListScenario::<T>::new(origin_weight, true)?; let scenario = ListScenario::<T>::new(origin_weight, true)?;
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap(), T::Staking::active_stake(&scenario.origin1).unwrap(),
origin_weight origin_weight
); );
@@ -234,7 +230,7 @@ frame_benchmarking::benchmarks! {
verify { verify {
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), joiner_free - max_additional); assert_eq!(CurrencyOf::<T>::free_balance(&joiner), joiner_free - max_additional);
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap(), T::Staking::active_stake(&scenario.origin1).unwrap(),
scenario.dest_weight scenario.dest_weight
); );
} }
@@ -249,7 +245,7 @@ frame_benchmarking::benchmarks! {
}: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra)) }: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra))
verify { verify {
assert!( assert!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap() >= T::Staking::active_stake(&scenario.origin1).unwrap() >=
scenario.dest_weight scenario.dest_weight
); );
} }
@@ -267,7 +263,7 @@ frame_benchmarking::benchmarks! {
}: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::Rewards) }: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::Rewards)
verify { verify {
assert!( assert!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap() >= T::Staking::active_stake(&scenario.origin1).unwrap() >=
scenario.dest_weight scenario.dest_weight
); );
} }
@@ -314,7 +310,7 @@ frame_benchmarking::benchmarks! {
whitelist_account!(member_id); whitelist_account!(member_id);
}: _(RuntimeOrigin::Signed(member_id.clone()), member_id_lookup, all_points) }: _(RuntimeOrigin::Signed(member_id.clone()), member_id_lookup, all_points)
verify { 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 // We at least went down to the destination bag
assert!(bonded_after <= scenario.dest_weight); assert!(bonded_after <= scenario.dest_weight);
let member = PoolMembers::<T>::get( let member = PoolMembers::<T>::get(
@@ -323,7 +319,7 @@ frame_benchmarking::benchmarks! {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
member.unbonding_eras.keys().cloned().collect::<Vec<_>>(), member.unbonding_eras.keys().cloned().collect::<Vec<_>>(),
vec![0 + T::StakingInterface::bonding_duration()] vec![0 + T::Staking::bonding_duration()]
); );
assert_eq!( assert_eq!(
member.unbonding_eras.values().cloned().collect::<Vec<_>>(), member.unbonding_eras.values().cloned().collect::<Vec<_>>(),
@@ -345,7 +341,7 @@ frame_benchmarking::benchmarks! {
// Sanity check join worked // Sanity check join worked
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(), T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond + min_join_bond min_create_bond + min_join_bond
); );
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), 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 // Sanity check that unbond worked
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(), T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond min_create_bond
); );
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1); assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
@@ -388,7 +384,7 @@ frame_benchmarking::benchmarks! {
// Sanity check join worked // Sanity check join worked
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(), T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond + min_join_bond min_create_bond + min_join_bond
); );
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), 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 // Sanity check that unbond worked
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(), T::Staking::active_stake(&pool_account).unwrap(),
min_create_bond min_create_bond
); );
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1); 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 // Sanity check that unbond worked
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(), T::Staking::active_stake(&pool_account).unwrap(),
Zero::zero() Zero::zero()
); );
assert_eq!( assert_eq!(
@@ -525,8 +521,8 @@ frame_benchmarking::benchmarks! {
} }
); );
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&Pools::<T>::create_bonded_account(1)), T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
Some(min_create_bond) Ok(min_create_bond)
); );
} }
@@ -564,8 +560,8 @@ frame_benchmarking::benchmarks! {
} }
); );
assert_eq!( assert_eq!(
T::StakingInterface::active_stake(&Pools::<T>::create_bonded_account(1)), T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
Some(min_create_bond) Ok(min_create_bond)
); );
} }
@@ -647,13 +643,13 @@ frame_benchmarking::benchmarks! {
.map(|i| account("stash", USER_SEED, i)) .map(|i| account("stash", USER_SEED, i))
.collect(); .collect();
assert_ok!(Pools::<T>::nominate(RuntimeOrigin::Signed(depositor.clone()).into(), 1, validators)); assert_ok!(T::Staking::nominate(&pool_account, validators));
assert!(T::StakingInterface::nominations(Pools::<T>::create_bonded_account(1)).is_some()); assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_some());
whitelist_account!(depositor); whitelist_account!(depositor);
}:_(RuntimeOrigin::Signed(depositor.clone()), 1) }:_(RuntimeOrigin::Signed(depositor.clone()), 1)
verify { 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!( impl_benchmark_test_suite!(
@@ -157,11 +157,10 @@ impl pallet_nomination_pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type WeightInfo = (); type WeightInfo = ();
type Currency = Balances; type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128; type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256; type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance; type U256ToBalance = U256ToBalance;
type StakingInterface = Staking; type Staking = Staking;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type MaxMetadataLen = ConstU32<256>; type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>; type MaxUnbonding = ConstU32<8>;
+27 -52
View File
@@ -286,7 +286,7 @@ use sp_runtime::{
AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup,
Zero, Zero,
}, },
FixedPointNumber, FixedPointOperand, FixedPointNumber,
}; };
use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_staking::{EraIndex, OnStakerSlash, StakingInterface};
use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; 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. /// 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. /// 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() { if let Some(pool) = BondedPool::<T>::get(self.pool_id).defensive() {
pool.points_to_balance(self.points) pool.points_to_balance(self.points)
} else { } else {
@@ -623,7 +623,7 @@ impl<T: Config> BondedPool<T> {
/// This is often used for bonding and issuing new funds into the pool. /// This is often used for bonding and issuing new funds into the pool.
fn balance_to_point(&self, new_funds: BalanceOf<T>) -> BalanceOf<T> { fn balance_to_point(&self, new_funds: BalanceOf<T>) -> BalanceOf<T> {
let bonded_balance = 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) 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. /// This is often used for unbonding.
fn points_to_balance(&self, points: BalanceOf<T>) -> BalanceOf<T> { fn points_to_balance(&self, points: BalanceOf<T>) -> BalanceOf<T> {
let bonded_balance = 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) 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> { fn transferrable_balance(&self) -> BalanceOf<T> {
let account = self.bonded_account(); let account = self.bonded_account();
T::Currency::free_balance(&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 { fn is_root(&self, who: &T::AccountId) -> bool {
@@ -738,7 +738,7 @@ impl<T: Config> BondedPool<T> {
ensure!(!self.is_destroying(), Error::<T>::CanNotChangeState); ensure!(!self.is_destroying(), Error::<T>::CanNotChangeState);
let bonded_balance = 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); ensure!(!bonded_balance.is_zero(), Error::<T>::OverflowRisk);
let points_to_balance_ratio_floor = self let points_to_balance_ratio_floor = self
@@ -791,7 +791,7 @@ impl<T: Config> BondedPool<T> {
target_member.active_points().saturating_sub(unbonding_points); target_member.active_points().saturating_sub(unbonding_points);
let mut target_member_after_unbond = (*target_member).clone(); let mut target_member_after_unbond = (*target_member).clone();
target_member_after_unbond.points = new_depositor_points; 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. // 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. /// Bond exactly `amount` from `who`'s funds into this pool.
/// ///
/// If the bond type is `Create`, `StakingInterface::bond` is called, and `who` /// If the bond type is `Create`, `Staking::bond` is called, and `who`
/// is allowed to be killed. Otherwise, `StakingInterface::bond_extra` is called and `who` /// is allowed to be killed. Otherwise, `Staking::bond_extra` is called and `who`
/// cannot be killed. /// cannot be killed.
/// ///
/// Returns `Ok(points_issues)`, `Err` otherwise. /// Returns `Ok(points_issues)`, `Err` otherwise.
@@ -889,16 +889,11 @@ impl<T: Config> BondedPool<T> {
let points_issued = self.issue(amount); let points_issued = self.issue(amount);
match ty { match ty {
BondType::Create => T::StakingInterface::bond( BondType::Create => T::Staking::bond(&bonded_account, amount, &self.reward_account())?,
bonded_account.clone(),
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 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 // the active balance is slashed below the minimum bonded or the account cannot be
// found, we exit early. // 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) 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 // NOTE: this may be dangerous in the scenario bonding_duration gets decreased because
// we would no longer be able to decode `UnbondingPoolsWithEra`, which uses // we would no longer be able to decode `UnbondingPoolsWithEra`, which uses
// `TotalUnbondingPools` as the bound // `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 super::*;
use frame_support::traits::StorageVersion; use frame_support::traits::StorageVersion;
use frame_system::{ensure_signed, pallet_prelude::*}; use frame_system::{ensure_signed, pallet_prelude::*};
use sp_runtime::traits::CheckedAdd;
/// The current storage version. /// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);
@@ -1157,20 +1151,7 @@ pub mod pallet {
type WeightInfo: weights::WeightInfo; type WeightInfo: weights::WeightInfo;
/// The nominating balance. /// The nominating balance.
type Currency: Currency<Self::AccountId, Balance = Self::CurrencyBalance>; type Currency: Currency<Self::AccountId>;
/// 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;
/// The type that is used for reward counter. /// The type that is used for reward counter.
/// ///
@@ -1212,10 +1193,7 @@ pub mod pallet {
type U256ToBalance: Convert<U256, BalanceOf<Self>>; type U256ToBalance: Convert<U256, BalanceOf<Self>>;
/// The interface for nominating. /// The interface for nominating.
type StakingInterface: StakingInterface< type Staking: StakingInterface<Balance = BalanceOf<Self>, AccountId = Self::AccountId>;
Balance = BalanceOf<Self>,
AccountId = Self::AccountId,
>;
/// The amount of eras a `SubPools::with_era` pool can exist before it gets merged into the /// 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 /// `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 _ = 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 _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?;
let current_era = T::StakingInterface::current_era(); let current_era = T::Staking::current_era();
let unbond_era = T::StakingInterface::bonding_duration().saturating_add(current_era); let unbond_era = T::Staking::bonding_duration().saturating_add(current_era);
// Unbond in the actual underlying nominator. // Unbond in the actual underlying nominator.
let unbonding_balance = bonded_pool.dissolve(unbonding_points); 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 // 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) 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 // 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. // is destroying then `withdraw_unbonded` can be used.
ensure!(pool.state != PoolState::Destroying, Error::<T>::NotDestroying); 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(()) Ok(())
} }
@@ -1754,7 +1732,7 @@ pub mod pallet {
let member_account = T::Lookup::lookup(member_account)?; let member_account = T::Lookup::lookup(member_account)?;
let mut member = let mut member =
PoolMembers::<T>::get(&member_account).ok_or(Error::<T>::PoolMemberNotFound)?; 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) let bonded_pool = BondedPool::<T>::get(member.pool_id)
.defensive_ok_or::<Error<T>>(DefensiveError::PoolNotFound.into())?; .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 // Before calculate the `balance_to_unbond`, with call withdraw unbonded to ensure the
// `transferrable_balance` is correct. // `transferrable_balance` is correct.
let stash_killed = T::StakingInterface::withdraw_unbonded( let stash_killed =
bonded_pool.bonded_account(), T::Staking::withdraw_unbonded(bonded_pool.bonded_account(), num_slashing_spans)?;
num_slashing_spans,
)?;
// defensive-only: the depositor puts enough funds into the stash so that it will only // defensive-only: the depositor puts enough funds into the stash so that it will only
// be destroyed when they are leaving. // be destroyed when they are leaving.
@@ -1960,7 +1936,7 @@ pub mod pallet {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?; let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator); 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. /// Set a new state for the pool.
@@ -2132,7 +2108,7 @@ pub mod pallet {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?; let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator); 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" "Minimum points to balance ratio must be greater than 0"
); );
assert!( 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 / "There must be more unbonding pools then the bonding duration /
so a slash can be applied to relevant unboding pools. (We assume / so a slash can be applied to relevant unboding pools. (We assume /
the bonding duration > slash deffer duration.", 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 /// 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. /// is coming from the staking pallet and the latter two are configured in this pallet.
pub fn depositor_min_bond() -> BalanceOf<T> { pub fn depositor_min_bond() -> BalanceOf<T> {
T::StakingInterface::minimum_bond() T::Staking::minimum_nominator_bond()
.max(MinCreateBond::<T>::get()) .max(MinCreateBond::<T>::get())
.max(MinJoinBond::<T>::get()) .max(MinJoinBond::<T>::get())
.max(T::Currency::minimum_balance()) .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(&reward_account), 0);
debug_assert_eq!(frame_system::Pallet::<T>::consumers(&bonded_account), 0); debug_assert_eq!(frame_system::Pallet::<T>::consumers(&bonded_account), 0);
debug_assert_eq!( debug_assert_eq!(
T::StakingInterface::total_stake(&bonded_account).unwrap_or_default(), T::Staking::total_stake(&bonded_account).unwrap_or_default(),
Zero::zero() Zero::zero()
); );
@@ -2487,8 +2463,7 @@ impl<T: Config> Pallet<T> {
let subs = SubPoolsStorage::<T>::get(pool_id).unwrap_or_default(); let subs = SubPoolsStorage::<T>::get(pool_id).unwrap_or_default();
let sum_unbonding_balance = subs.sum_unbonding_balance(); let sum_unbonding_balance = subs.sum_unbonding_balance();
let bonded_balance = let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default();
T::StakingInterface::active_stake(&pool_account).unwrap_or_default();
let total_balance = T::Currency::total_balance(&pool_account); let total_balance = T::Currency::total_balance(&pool_account);
assert!( assert!(
+61 -32
View File
@@ -3,6 +3,7 @@ use crate::{self as pools};
use frame_support::{assert_ok, parameter_types, PalletId}; use frame_support::{assert_ok, parameter_types, PalletId};
use frame_system::RawOrigin; use frame_system::RawOrigin;
use sp_runtime::FixedU128; use sp_runtime::FixedU128;
use sp_staking::Stake;
pub type BlockNumber = u64; pub type BlockNumber = u64;
pub type AccountId = u128; pub type AccountId = u128;
@@ -47,9 +48,16 @@ impl sp_staking::StakingInterface for StakingMock {
type Balance = Balance; type Balance = Balance;
type AccountId = AccountId; type AccountId = AccountId;
fn minimum_bond() -> Self::Balance { fn minimum_nominator_bond() -> Self::Balance {
StakingMinBond::get() 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 { fn current_era() -> EraIndex {
CurrentEra::get() CurrentEra::get()
@@ -59,39 +67,24 @@ impl sp_staking::StakingInterface for StakingMock {
BondingDuration::get() BondingDuration::get()
} }
fn active_stake(who: &Self::AccountId) -> Option<Self::Balance> { fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
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 {
let mut x = BondedBalanceMap::get(); let mut x = BondedBalanceMap::get();
x.get_mut(&who).map(|v| *v += extra); x.get_mut(who).map(|v| *v += extra);
BondedBalanceMap::set(&x); BondedBalanceMap::set(&x);
Ok(()) Ok(())
} }
fn unbond(who: Self::AccountId, amount: Self::Balance) -> DispatchResult { fn unbond(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult {
let mut x = BondedBalanceMap::get(); 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); BondedBalanceMap::set(&x);
let mut y = UnbondingBalanceMap::get(); 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); UnbondingBalanceMap::set(&y);
Ok(()) Ok(())
} }
fn chill(_: Self::AccountId) -> sp_runtime::DispatchResult { fn chill(_: &Self::AccountId) -> sp_runtime::DispatchResult {
Ok(()) Ok(())
} }
@@ -104,17 +97,12 @@ impl sp_staking::StakingInterface for StakingMock {
Ok(UnbondingBalanceMap::get().is_empty() && BondedBalanceMap::get().is_empty()) Ok(UnbondingBalanceMap::get().is_empty() && BondedBalanceMap::get().is_empty())
} }
fn bond( fn bond(stash: &Self::AccountId, value: Self::Balance, _: &Self::AccountId) -> DispatchResult {
stash: Self::AccountId, StakingMock::set_bonded_balance(*stash, value);
_: Self::AccountId,
value: Self::Balance,
_: Self::AccountId,
) -> DispatchResult {
StakingMock::set_bonded_balance(stash, value);
Ok(()) Ok(())
} }
fn nominate(_: Self::AccountId, nominations: Vec<Self::AccountId>) -> DispatchResult { fn nominate(_: &Self::AccountId, nominations: Vec<Self::AccountId>) -> DispatchResult {
Nominations::set(&Some(nominations)); Nominations::set(&Some(nominations));
Ok(()) Ok(())
} }
@@ -123,6 +111,48 @@ impl sp_staking::StakingInterface for StakingMock {
fn nominations(_: Self::AccountId) -> Option<Vec<Self::AccountId>> { fn nominations(_: Self::AccountId) -> Option<Vec<Self::AccountId>> {
Nominations::get() 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 { impl frame_system::Config for Runtime {
@@ -192,11 +222,10 @@ impl pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type WeightInfo = (); type WeightInfo = ();
type Currency = Balances; type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = RewardCounter; type RewardCounter = RewardCounter;
type BalanceToU256 = BalanceToU256; type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance; type U256ToBalance = U256ToBalance;
type StakingInterface = StakingMock; type Staking = StakingMock;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type PalletId = PoolsPalletId; type PalletId = PoolsPalletId;
type MaxMetadataLen = MaxMetadataLen; type MaxMetadataLen = MaxMetadataLen;
+16 -13
View File
@@ -3083,18 +3083,18 @@ mod pool_withdraw_unbonded {
fn pool_withdraw_unbonded_works() { fn pool_withdraw_unbonded_works() {
ExtBuilder::default().build_and_execute(|| { ExtBuilder::default().build_and_execute(|| {
// Given 10 unbond'ed directly against the pool account // 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 // and the pool account only has 10 balance
assert_eq!(StakingMock::active_stake(&default_bonded_account()), Some(5)); assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Some(10)); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(10));
assert_eq!(Balances::free_balance(&default_bonded_account()), 10); assert_eq!(Balances::free_balance(&default_bonded_account()), 10);
// When // When
assert_ok!(Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0)); assert_ok!(Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0));
// Then there unbonding balance is no longer locked // Then there unbonding balance is no longer locked
assert_eq!(StakingMock::active_stake(&default_bonded_account()), Some(5)); assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5));
assert_eq!(StakingMock::total_stake(&default_bonded_account()), Some(5)); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(5));
assert_eq!(Balances::free_balance(&default_bonded_account()), 10); 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. // current bond is 600, we slash it all to 300.
StakingMock::set_bonded_balance(default_bonded_account(), 300); StakingMock::set_bonded_balance(default_bonded_account(), 300);
Balances::make_free_balance_be(&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(40));
assert_ok!(fully_unbond_permissioned(550)); assert_ok!(fully_unbond_permissioned(550));
@@ -4074,12 +4074,15 @@ mod create {
assert!(!BondedPools::<Runtime>::contains_key(2)); assert!(!BondedPools::<Runtime>::contains_key(2));
assert!(!RewardPools::<Runtime>::contains_key(2)); assert!(!RewardPools::<Runtime>::contains_key(2));
assert!(!PoolMembers::<Runtime>::contains_key(11)); 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( assert_ok!(Pools::create(
RuntimeOrigin::signed(11), RuntimeOrigin::signed(11),
StakingMock::minimum_bond(), StakingMock::minimum_nominator_bond(),
123, 123,
456, 456,
789 789
@@ -4090,7 +4093,7 @@ mod create {
PoolMembers::<Runtime>::get(11).unwrap(), PoolMembers::<Runtime>::get(11).unwrap(),
PoolMember { PoolMember {
pool_id: 2, pool_id: 2,
points: StakingMock::minimum_bond(), points: StakingMock::minimum_nominator_bond(),
..Default::default() ..Default::default()
} }
); );
@@ -4099,7 +4102,7 @@ mod create {
BondedPool { BondedPool {
id: 2, id: 2,
inner: BondedPoolInner { inner: BondedPoolInner {
points: StakingMock::minimum_bond(), points: StakingMock::minimum_nominator_bond(),
member_counter: 1, member_counter: 1,
state: PoolState::Open, state: PoolState::Open,
roles: PoolRoles { roles: PoolRoles {
@@ -4113,7 +4116,7 @@ mod create {
); );
assert_eq!( assert_eq!(
StakingMock::active_stake(&next_pool_stash).unwrap(), StakingMock::active_stake(&next_pool_stash).unwrap(),
StakingMock::minimum_bond() StakingMock::minimum_nominator_bond()
); );
assert_eq!( assert_eq!(
RewardPools::<Runtime>::get(2).unwrap(), RewardPools::<Runtime>::get(2).unwrap(),
@@ -4142,7 +4145,7 @@ mod create {
// Given // Given
assert_eq!(MinCreateBond::<Runtime>::get(), 2); assert_eq!(MinCreateBond::<Runtime>::get(), 2);
assert_eq!(StakingMock::minimum_bond(), 10); assert_eq!(StakingMock::minimum_nominator_bond(), 10);
// Then // Then
assert_noop!( assert_noop!(
@@ -171,11 +171,10 @@ impl pallet_nomination_pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type WeightInfo = (); type WeightInfo = ();
type Currency = Balances; type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128; type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256; type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance; type U256ToBalance = U256ToBalance;
type StakingInterface = Staking; type Staking = Staking;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type MaxMetadataLen = ConstU32<256>; type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>; type MaxUnbonding = ConstU32<8>;
+80 -27
View File
@@ -18,8 +18,8 @@
//! Implementations for the Staking FRAME Pallet. //! Implementations for the Staking FRAME Pallet.
use frame_election_provider_support::{ use frame_election_provider_support::{
data_provider, ElectionDataProvider, ElectionProvider, ScoreProvider, SortedListProvider, data_provider, ElectionDataProvider, ElectionProvider, ElectionProviderBase, ScoreProvider,
Supports, VoteWeight, VoterOf, SortedListProvider, Supports, VoteWeight, VoterOf,
}; };
use frame_support::{ use frame_support::{
dispatch::WithPostDispatchInfo, dispatch::WithPostDispatchInfo,
@@ -38,7 +38,7 @@ use sp_runtime::{
}; };
use sp_staking::{ use sp_staking::{
offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler},
EraIndex, SessionIndex, StakingInterface, EraIndex, SessionIndex, Stake, StakingInterface,
}; };
use sp_std::{collections::btree_map::BTreeMap, prelude::*}; 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> { impl<T: Config> StakingInterface for Pallet<T> {
type AccountId = T::AccountId; type AccountId = T::AccountId;
type Balance = BalanceOf<T>; type Balance = BalanceOf<T>;
fn minimum_bond() -> Self::Balance { fn minimum_nominator_bond() -> Self::Balance {
MinNominatorBond::<T>::get() 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 { fn bonding_duration() -> EraIndex {
T::BondingDuration::get() T::BondingDuration::get()
} }
@@ -1498,58 +1528,81 @@ impl<T: Config> StakingInterface for Pallet<T> {
Self::current_era().unwrap_or(Zero::zero()) Self::current_era().unwrap_or(Zero::zero())
} }
fn active_stake(controller: &Self::AccountId) -> Option<Self::Balance> { fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError> {
Self::ledger(controller).map(|l| l.active) 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> { fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
Self::ledger(controller).map(|l| l.total) Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra)
} }
fn bond_extra(stash: Self::AccountId, extra: Self::Balance) -> DispatchResult { fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
Self::bond_extra(RawOrigin::Signed(stash).into(), extra) 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 { fn chill(who: &Self::AccountId) -> DispatchResult {
Self::unbond(RawOrigin::Signed(controller).into(), value) // 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)?;
fn chill(controller: Self::AccountId) -> DispatchResult { Self::chill(RawOrigin::Signed(ctrl).into())
Self::chill(RawOrigin::Signed(controller).into())
} }
fn withdraw_unbonded( fn withdraw_unbonded(
controller: Self::AccountId, who: Self::AccountId,
num_slashing_spans: u32, num_slashing_spans: u32,
) -> Result<bool, DispatchError> { ) -> Result<bool, DispatchError> {
Self::withdraw_unbonded(RawOrigin::Signed(controller.clone()).into(), num_slashing_spans) let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
.map(|_| !Ledger::<T>::contains_key(&controller)) Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans)
.map(|_| !Ledger::<T>::contains_key(&ctrl))
.map_err(|with_post| with_post.error) .map_err(|with_post| with_post.error)
} }
fn bond( fn bond(
stash: Self::AccountId, who: &Self::AccountId,
controller: Self::AccountId,
value: Self::Balance, value: Self::Balance,
payee: Self::AccountId, payee: &Self::AccountId,
) -> DispatchResult { ) -> DispatchResult {
Self::bond( Self::bond(
RawOrigin::Signed(stash).into(), RawOrigin::Signed(who.clone()).into(),
T::Lookup::unlookup(controller), T::Lookup::unlookup(who.clone()),
value, 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<_>>(); 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")] #[cfg(feature = "runtime-benchmarks")]
fn nominations(who: Self::AccountId) -> Option<Vec<T::AccountId>> { fn nominations(who: Self::AccountId) -> Option<Vec<T::AccountId>> {
Nominators::<T>::get(who).map(|n| n.targets.into_inner()) 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"))] #[cfg(any(test, feature = "try-runtime"))]
+1 -1
View File
@@ -262,7 +262,7 @@ pub mod pallet {
type WeightInfo: WeightInfo; type WeightInfo: WeightInfo;
} }
/// The ideal number of staking participants. /// The ideal number of active validators.
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn validator_count)] #[pallet::getter(fn validator_count)]
pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>; 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 //! A crate which contains primitives that are useful for implementation that uses staking
//! approaches in general. Definitions related to sessions, slashing, etc go here. //! approaches in general. Definitions related to sessions, slashing, etc go here.
use sp_runtime::{DispatchError, DispatchResult}; use sp_runtime::{DispatchError, DispatchResult};
use sp_std::collections::btree_map::BTreeMap; use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
pub mod offence; 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 { pub trait StakingInterface {
/// Balance type used by the staking system. /// Balance type used by the staking system.
type Balance; type Balance: PartialEq;
/// AccountId type used by the staking system /// AccountId type used by the staking system
type AccountId; type AccountId;
/// The minimum amount required to bond in order to be a nominator. This does not necessarily /// The minimum amount required to bond in order to set nomination intentions. This does not
/// mean the nomination will be counted in an election, but instead just enough to be stored as /// necessarily mean the nomination will be counted in an election, but instead just enough to
/// a nominator. In other words, this is the minimum amount to register the intention to /// be stored as a nominator. In other words, this is the minimum amount to register the
/// nominate. /// intention to nominate.
fn minimum_bond() -> Self::Balance; 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. /// 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; fn bonding_duration() -> EraIndex;
/// The current era index. /// The current era index.
@@ -80,41 +111,39 @@ pub trait StakingInterface {
/// This should be the latest planned era that the staking system knows about. /// This should be the latest planned era that the staking system knows about.
fn current_era() -> EraIndex; fn current_era() -> EraIndex;
/// The amount of active stake that `stash` has in the staking system. /// Returns the stake of `who`.
fn active_stake(stash: &Self::AccountId) -> Option<Self::Balance>; fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError>;
/// The total stake that `stash` has in the staking system. This includes the fn total_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
/// [`Self::active_stake`], and any funds currently in the process of unbonding via Self::stake(who).map(|s| s.total)
/// [`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>;
/// Bond (lock) `value` of `stash`'s balance. `controller` will be set as the account fn active_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
/// controlling `stash`. This creates what is referred to as "bonded pair". Self::stake(who).map(|s| s.active)
fn bond( }
stash: Self::AccountId,
controller: Self::AccountId,
value: Self::Balance,
payee: Self::AccountId,
) -> DispatchResult;
/// Have `controller` nominate `validators`. fn is_unbonding(who: &Self::AccountId) -> Result<bool, DispatchError> {
fn nominate( Self::stake(who).map(|s| s.active != s.total)
controller: Self::AccountId, }
validators: sp_std::vec::Vec<Self::AccountId>,
) -> DispatchResult;
/// Chill `stash`. fn fully_unbond(who: &Self::AccountId) -> DispatchResult {
fn chill(controller: 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 /// Bond (lock) `value` of `who`'s balance, while forwarding any rewards to `payee`.
/// the account. The amount extra actually bonded will never be more than the _Stash_'s free 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. /// 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 /// Schedule a portion of the active bonded balance to be unlocked at era
/// [Self::current_era] + [`Self::bonding_duration`]. /// [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 /// 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 /// 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. /// 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. /// Unlock any funds schedule to unlock before or at the current era.
/// ///
@@ -135,7 +164,29 @@ pub trait StakingInterface {
num_slashing_spans: u32, num_slashing_spans: u32,
) -> Result<bool, DispatchError>; ) -> 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. /// Get the nominations of a stash, if they are a nominator, `None` otherwise.
#[cfg(feature = "runtime-benchmarks")] #[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);
} }