Allow privileged virtual bond in Staking pallet (#3889)

This is the first PR in preparation for
https://github.com/paritytech/polkadot-sdk/issues/454.

## Follow ups:
- https://github.com/paritytech/polkadot-sdk/pull/3904.
- https://github.com/paritytech/polkadot-sdk/pull/3905.

Overall changes are documented here (lot more visual 😍):
https://hackmd.io/@ak0n/454-np-governance

[Maybe followup](https://github.com/paritytech/polkadot-sdk/issues/4217)
with migration of storage item `VirtualStakers` as a bool or enum in
`Ledger`.

## Context
We want to achieve a way for a user (`Delegator`) to delegate their
funds to another account (`Agent`). Delegate implies the funds are
locked in delegator account itself. Agent can act on behalf of delegator
to stake directly on Staking pallet.

The delegation feature is added to Staking via another pallet
`delegated-staking` worked on
[here](https://github.com/paritytech/polkadot-sdk/pull/3904).

## Introduces:
### StakingUnchecked Trait
As the name implies, this trait allows unchecked (non-locked) mutation
of staking ledger. These apis are only meant to be used by other pallets
in the runtime and should not be exposed directly to user code path.
Also related: https://github.com/paritytech/polkadot-sdk/issues/3888.

### Virtual Bond
Allows other pallets to stake via staking pallet while managing the
locks on these accounts themselves. Introduces another storage
`VirtualStakers` that whitelist these accounts.

We also restrict virtual stakers to set reward account as themselves.
Since the account has no locks, we cannot support compounding of
rewards. Conservatively, we require them to set a separate account
different from the staker. Since these are code managed, it should be
easy for another pallet to redistribute reward and rebond them.

### Slashes
Since there is no actual lock maintained by staking-pallet for virtual
stakers, this pallet does not apply any slashes. It is then important
for pallets managing virtual stakers to listen to slashing events and
apply necessary slashes.
This commit is contained in:
Ankan
2024-04-20 02:05:34 +02:00
committed by GitHub
parent 4eabe5e0dd
commit e504c41a5a
9 changed files with 514 additions and 73 deletions
+36 -2
View File
@@ -29,7 +29,7 @@ use core::ops::Sub;
use scale_info::TypeInfo;
use sp_runtime::{
traits::{AtLeast32BitUnsigned, Zero},
DispatchError, DispatchResult, RuntimeDebug, Saturating,
DispatchError, DispatchResult, Perbill, RuntimeDebug, Saturating,
};
pub mod offence;
@@ -254,6 +254,9 @@ pub trait StakingInterface {
/// schedules have reached their unlocking era should allow more calls to this function.
fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult;
/// Update the reward destination for the ledger associated with the stash.
fn update_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult;
/// Unlock any funds schedule to unlock before or at the current era.
///
/// Returns whether the stash was killed because of this withdraw or not.
@@ -274,7 +277,7 @@ 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.
/// Return the status of the given staker, `Err` if not staked at all.
fn status(who: &Self::AccountId) -> Result<StakerStatus<Self::AccountId>, DispatchError>;
/// Checks whether or not this is a validator account.
@@ -290,6 +293,9 @@ pub trait StakingInterface {
}
}
/// Returns the fraction of the slash to be rewarded to reporter.
fn slash_reward_fraction() -> Perbill;
#[cfg(feature = "runtime-benchmarks")]
fn max_exposure_page_size() -> Page;
@@ -304,6 +310,34 @@ pub trait StakingInterface {
fn set_current_era(era: EraIndex);
}
/// Set of low level apis to manipulate staking ledger.
///
/// These apis bypass some or all safety checks and should only be used if you know what you are
/// doing.
pub trait StakingUnchecked: StakingInterface {
/// Migrate an existing staker to a virtual staker.
///
/// It would release all funds held by the implementation pallet.
fn migrate_to_virtual_staker(who: &Self::AccountId);
/// Book-keep a new bond for `keyless_who` without applying any locks (hence virtual).
///
/// It is important that `keyless_who` is a keyless account and therefore cannot interact with
/// staking pallet directly. Caller is responsible for ensuring the passed amount is locked and
/// valid.
fn virtual_bond(
keyless_who: &Self::AccountId,
value: Self::Balance,
payee: &Self::AccountId,
) -> DispatchResult;
/// Migrate a virtual staker to a direct staker.
///
/// Only used for testing.
#[cfg(feature = "runtime-benchmarks")]
fn migrate_to_direct_staker(who: &Self::AccountId);
}
/// The amount of exposure for an era that an individual nominator has (susceptible to slashing).
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct IndividualExposure<AccountId, Balance: HasCompact> {