Skip to content

Commit

Permalink
improve staking interface methods (paritytech#14023)
Browse files Browse the repository at this point in the history
* improve staking interface methods

* fix

* fix

* fix build

* restart

* fix

---------

Co-authored-by: parity-processbot <>
  • Loading branch information
kianenigma authored Apr 27, 2023
1 parent 04248e2 commit 58be496
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 24 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions frame/nomination-pools/benchmarking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,12 +684,12 @@ frame_benchmarking::benchmarks! {
.collect();

assert_ok!(T::Staking::nominate(&pool_account, validators));
assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_some());
assert!(T::Staking::nominations(&Pools::<T>::create_bonded_account(1)).is_some());

whitelist_account!(depositor);
}:_(RuntimeOrigin::Signed(depositor.clone()), 1)
verify {
assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_none());
assert!(T::Staking::nominations(&Pools::<T>::create_bonded_account(1)).is_none());
}

set_commission {
Expand Down
12 changes: 10 additions & 2 deletions frame/nomination-pools/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ impl sp_staking::StakingInterface for StakingMock {
BondingDuration::get()
}

fn status(
_: &Self::AccountId,
) -> Result<sp_staking::StakerStatus<Self::AccountId>, DispatchError> {
Nominations::get()
.map(|noms| sp_staking::StakerStatus::Nominator(noms))
.ok_or(DispatchError::Other("NotStash"))
}

fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
let mut x = BondedBalanceMap::get();
x.get_mut(who).map(|v| *v += extra);
Expand Down Expand Up @@ -108,15 +116,15 @@ impl sp_staking::StakingInterface for StakingMock {
}

#[cfg(feature = "runtime-benchmarks")]
fn nominations(_: Self::AccountId) -> Option<Vec<Self::AccountId>> {
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> {
fn stake(who: &Self::AccountId) -> Result<Stake<Balance>, DispatchError> {
match (
UnbondingBalanceMap::get().get(who).copied(),
BondedBalanceMap::get().get(who).copied(),
Expand Down
13 changes: 1 addition & 12 deletions frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ use sp_runtime::{
traits::{AtLeast32BitUnsigned, Convert, Saturating, StaticLookup, Zero},
Perbill, Perquintill, Rounding, RuntimeDebug,
};
pub use sp_staking::StakerStatus;
use sp_staking::{
offence::{Offence, OffenceError, ReportOffence},
EraIndex, SessionIndex,
Expand Down Expand Up @@ -381,18 +382,6 @@ impl<AccountId: Ord> Default for EraRewardPoints<AccountId> {
}
}

/// Indicates the initial status of the staker.
#[derive(RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, Clone))]
pub enum StakerStatus<AccountId> {
/// Chilling.
Idle,
/// Declared desire in validating or already participating in it.
Validator,
/// Nominating for a group of other stakers.
Nominator(Vec<AccountId>),
}

/// A destination account for payment.
#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum RewardDestination<AccountId> {
Expand Down
30 changes: 28 additions & 2 deletions frame/staking/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use frame_election_provider_support::{
SortedListProvider, VoteWeight, VoterOf,
};
use frame_support::{
defensive,
dispatch::WithPostDispatchInfo,
pallet_prelude::*,
traits::{
Expand Down Expand Up @@ -1608,7 +1609,7 @@ impl<T: Config> StakingInterface for Pallet<T> {
Self::current_era().unwrap_or(Zero::zero())
}

fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError> {
fn stake(who: &Self::AccountId) -> Result<Stake<BalanceOf<T>>, DispatchError> {
Self::bonded(who)
.and_then(|c| Self::ledger(c))
.map(|l| Stake { total: l.total, active: l.active })
Expand Down Expand Up @@ -1662,8 +1663,33 @@ impl<T: Config> StakingInterface for Pallet<T> {
Self::nominate(RawOrigin::Signed(ctrl).into(), targets)
}

fn status(
who: &Self::AccountId,
) -> Result<sp_staking::StakerStatus<Self::AccountId>, DispatchError> {
let is_bonded = Self::bonded(who).is_some();
if !is_bonded {
return Err(Error::<T>::NotStash.into())
}

let is_validator = Validators::<T>::contains_key(&who);
let is_nominator = Nominators::<T>::get(&who);

use sp_staking::StakerStatus;
match (is_validator, is_nominator.is_some()) {
(false, false) => Ok(StakerStatus::Idle),
(true, false) => Ok(StakerStatus::Validator),
(false, true) => Ok(StakerStatus::Nominator(
is_nominator.expect("is checked above; qed").targets.into_inner(),
)),
(true, true) => {
defensive!("cannot be both validators and nominator");
Err(Error::<T>::BadState.into())
},
}
}

sp_staking::runtime_benchmarks_enabled! {
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())
}

Expand Down
23 changes: 23 additions & 0 deletions frame/staking/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5824,4 +5824,27 @@ mod staking_interface {
));
});
}

#[test]
fn status() {
ExtBuilder::default().build_and_execute(|| {
// stash of a validator is identified as a validator
assert_eq!(Staking::status(&11).unwrap(), StakerStatus::Validator);
// .. but not the controller.
assert!(Staking::status(&10).is_err());

// stash of nominator is identified as a nominator
assert_eq!(Staking::status(&101).unwrap(), StakerStatus::Nominator(vec![11, 21]));
// .. but not the controller.
assert!(Staking::status(&100).is_err());

// stash of chilled is identified as a chilled
assert_eq!(Staking::status(&41).unwrap(), StakerStatus::Idle);
// .. but not the controller.
assert!(Staking::status(&40).is_err());

// random other account.
assert!(Staking::status(&42).is_err());
})
}
}
2 changes: 2 additions & 0 deletions primitives/staking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
serde = { version = "1.0.136", optional = true }
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] }
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
sp-core = { version = "7.0.0", default-features = false, path = "../core" }
Expand All @@ -22,6 +23,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../std" }
[features]
default = ["std"]
std = [
"serde",
"codec/std",
"scale-info/std",
"sp-core/std",
Expand Down
38 changes: 32 additions & 6 deletions primitives/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
//! 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 scale_info::TypeInfo;
use sp_core::RuntimeDebug;
use sp_runtime::{DispatchError, DispatchResult};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};

Expand All @@ -31,6 +33,18 @@ pub type SessionIndex = u32;
/// Counter for the number of eras that have passed.
pub type EraIndex = u32;

/// Representation of the status of a staker.
#[derive(RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone))]
pub enum StakerStatus<AccountId> {
/// Chilling.
Idle,
/// Declaring desire in validate, i.e author blocks.
Validator,
/// Declaring desire to nominate, delegate, or generally approve of the given set of others.
Nominator(Vec<AccountId>),
}

/// Trait describing something that implements a hook for any operations to perform when a staker is
/// slashed.
pub trait OnStakerSlash<AccountId, Balance> {
Expand All @@ -57,7 +71,7 @@ impl<AccountId, Balance> OnStakerSlash<AccountId, Balance> for () {

/// 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> {
pub struct Stake<Balance> {
/// 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`].
Expand All @@ -67,10 +81,10 @@ pub struct Stake<T: StakingInterface + ?Sized> {
/// 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,
pub total: Balance,
/// The total amount of the stash's balance that will be at stake in any forthcoming
/// rounds.
pub active: T::Balance,
pub active: Balance,
}

/// A generic representation of a staking implementation.
Expand Down Expand Up @@ -109,21 +123,25 @@ pub trait StakingInterface {
/// This should be the latest planned era that the staking system knows about.
fn current_era() -> EraIndex;

/// Returns the stake of `who`.
fn stake(who: &Self::AccountId) -> Result<Stake<Self>, DispatchError>;
/// Returns the [`Stake`] of `who`.
fn stake(who: &Self::AccountId) -> Result<Stake<Self::Balance>, DispatchError>;

/// Total stake of a staker, `Err` if not a staker.
fn total_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
Self::stake(who).map(|s| s.total)
}

/// Total active portion of a staker's [`Stake`], `Err` if not a staker.
fn active_stake(who: &Self::AccountId) -> Result<Self::Balance, DispatchError> {
Self::stake(who).map(|s| s.active)
}

/// Returns whether a staker is unbonding, `Err` if not a staker at all.
fn is_unbonding(who: &Self::AccountId) -> Result<bool, DispatchError> {
Self::stake(who).map(|s| s.active != s.total)
}

/// Returns whether a staker is FULLY unbonding, `Err` if not a staker at all.
fn fully_unbond(who: &Self::AccountId) -> DispatchResult {
Self::unbond(who, Self::stake(who)?.active)
}
Expand Down Expand Up @@ -174,9 +192,17 @@ pub trait StakingInterface {
/// Checks whether an account `staker` has been exposed in an era.
fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool;

/// Return the status of the given staker, `None` if not staked at all.
fn status(who: &Self::AccountId) -> Result<StakerStatus<Self::AccountId>, DispatchError>;

/// Get the nominations of a stash, if they are a nominator, `None` otherwise.
#[cfg(feature = "runtime-benchmarks")]
fn nominations(who: Self::AccountId) -> Option<Vec<Self::AccountId>>;
fn nominations(who: &Self::AccountId) -> Option<Vec<Self::AccountId>> {
match Self::status(who) {
Ok(StakerStatus::Nominator(t)) => Some(t),
_ => None,
}
}

#[cfg(feature = "runtime-benchmarks")]
fn add_era_stakers(
Expand Down

0 comments on commit 58be496

Please sign in to comment.