Refactor staking ledger (#1484)

This PR refactors the staking ledger logic to encapsulate all reads and
mutations of `Ledger`, `Bonded`, `Payee` and stake locks within the
`StakingLedger` struct implementation.

With these changes, all the reads and mutations to the `Ledger`, `Payee`
and `Bonded` storage map should be done through the methods exposed by
StakingLedger to ensure the data and lock consistency of the operations.
The new introduced methods that mutate and read Ledger are:

- `ledger.update()`: inserts/updates a staking ledger in storage;
updates staking locks accordingly (and ledger.bond(), which is synthatic
sugar for ledger.update())
- `ledger.kill()`: removes all Bonded and StakingLedger related data for
a given ledger; updates staking locks accordingly;
`StakingLedger::get(account)`: queries both the `Bonded` and `Ledger`
storages and returns a `Option<StakingLedger>`. The pallet impl exposes
fn ledger(account) as synthatic sugar for `StakingLedger::get(account)`.

Retrieving a ledger with `StakingLedger::get()` can be done by providing
either a stash or controller account. The input must be wrapped in a
`StakingAccount` variant (Stash or Controller) which is treated
accordingly. This simplifies the caller API but will eventually be
deprecated once we completely get rid of the controller account in
staking. However, this refactor will help with the work necessary when
completely removing the controller.

Other goals:

- No logical changes have been introduced in this PR;
- No breaking changes or updates in wallets required;
- No new storage items or need to perform storage migrations;
- Centralise the changes to bonds and ledger updates to simplify the
OnStakingUpdate updates to the target list (related to
https://github.com/paritytech/polkadot-sdk/issues/443)

Note: it would be great to prevent or at least raise a warning if
`Ledger<T>`, `Payee<T>` and `Bonded<T>` storage types are accessed
outside the `StakingLedger` implementation. This PR should not get
blocked by that feature, but there's a tracking issue here
https://github.com/paritytech/polkadot-sdk/issues/149

Related and step towards
https://github.com/paritytech/polkadot-sdk/issues/443
This commit is contained in:
Gonçalo Pestana
2023-10-15 22:50:07 +02:00
committed by GitHub
parent 1b34571c0c
commit 8ee4042c3b
12 changed files with 790 additions and 411 deletions
+23 -17
View File
@@ -91,7 +91,7 @@
//! #### Nomination
//!
//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on
//! a set of validators to be elected. Once interest in nomination is stated by an account, it
//! a set of validators to be elected. Once interest in nomination is stated by an account, it
//! takes effect at the next election round. The funds in the nominator's stash account indicate the
//! _weight_ of its vote. Both the rewards and any punishment that a validator earns are shared
//! between the validator and its nominators. This rule incentivizes the nominators to NOT vote for
@@ -294,6 +294,7 @@ mod tests;
pub mod election_size_tracker;
pub mod inflation;
pub mod ledger;
pub mod migrations;
pub mod slashing;
pub mod weights;
@@ -302,26 +303,27 @@ mod pallet;
use codec::{Decode, Encode, HasCompact, MaxEncodedLen};
use frame_support::{
traits::{ConstU32, Currency, Defensive, Get},
traits::{ConstU32, Currency, Defensive, Get, LockIdentifier},
weights::Weight,
BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
use scale_info::TypeInfo;
use sp_runtime::{
curve::PiecewiseLinear,
traits::{AtLeast32BitUnsigned, Convert, Saturating, StaticLookup, Zero},
Perbill, Perquintill, Rounding, RuntimeDebug,
traits::{AtLeast32BitUnsigned, Convert, StaticLookup, Zero},
Perbill, Perquintill, Rounding, RuntimeDebug, Saturating,
};
pub use sp_staking::StakerStatus;
use sp_staking::{
offence::{Offence, OffenceError, ReportOffence},
EraIndex, OnStakingUpdate, SessionIndex,
EraIndex, OnStakingUpdate, SessionIndex, StakingAccount,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
pub use weights::WeightInfo;
pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap};
pub(crate) const STAKING_ID: LockIdentifier = *b"staking ";
pub(crate) const LOG_TARGET: &str = "runtime::staking";
// syntactic sugar for logging.
@@ -433,6 +435,14 @@ pub struct UnlockChunk<Balance: HasCompact + MaxEncodedLen> {
}
/// The ledger of a (bonded) stash.
///
/// Note: All the reads and mutations to the [`Ledger`], [`Bonded`] and [`Payee`] storage items
/// *MUST* be performed through the methods exposed by this struct, to ensure the consistency of
/// ledger's data and corresponding staking lock
///
/// TODO: move struct definition and full implementation into `/src/ledger.rs`. Currently
/// leaving here to enforce a clean PR diff, given how critical this logic is. Tracking issue
/// <https://github.com/paritytech/substrate/issues/14749>.
#[derive(
PartialEqNoBound,
EqNoBound,
@@ -462,20 +472,15 @@ pub struct StakingLedger<T: Config> {
/// List of eras for which the stakers behind a validator have claimed rewards. Only updated
/// for validators.
pub claimed_rewards: BoundedVec<EraIndex, T::HistoryDepth>,
/// The controller associated with this ledger's stash.
///
/// This is not stored on-chain, and is only bundled when the ledger is read from storage.
/// Use [`controller`] function to get the controller associated with the ledger.
#[codec(skip)]
controller: Option<T::AccountId>,
}
impl<T: Config> StakingLedger<T> {
/// Initializes the default object using the given `validator`.
pub fn default_from(stash: T::AccountId) -> Self {
Self {
stash,
total: Zero::zero(),
active: Zero::zero(),
unlocking: Default::default(),
claimed_rewards: Default::default(),
}
}
/// Remove entries from `unlocking` that are sufficiently old and reduce the
/// total by the sum of their balances.
fn consolidate_unlocked(self, current_era: EraIndex) -> Self {
@@ -503,6 +508,7 @@ impl<T: Config> StakingLedger<T> {
active: self.active,
unlocking,
claimed_rewards: self.claimed_rewards,
controller: self.controller,
}
}
@@ -927,7 +933,7 @@ pub struct StashOf<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Convert<T::AccountId, Option<T::AccountId>> for StashOf<T> {
fn convert(controller: T::AccountId) -> Option<T::AccountId> {
<Pallet<T>>::ledger(&controller).map(|l| l.stash)
StakingLedger::<T>::paired_account(StakingAccount::Controller(controller))
}
}