Skip to content

Commit

Permalink
Refactor Nomination Pool to support multiple staking strategies (#3905)
Browse files Browse the repository at this point in the history
Third and final PR in the set, closes
#454.

Original PR: #2680

## Precursors:
- #3889.
- #3904.

## Follow up issues/improvements
- #4404

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

## Summary of various roles 🤯
### Pallet Staking
**Nominator**: An account that directly stakes on `pallet-staking` and
nominates a set of validators.
**Stakers**: Common term for nominators and validators.
Virtual Stakers: Same as stakers, but they are keyless accounts and
their locks are managed by a pallet external to `pallet-staking`.

### Pallet Delegated Staking
**Agent**: An account that receives delegation from other accounts
(delegators) and stakes on their behalf. They are also Virtual Stakers
in `pallet-staking` where `pallet-delegated-staking` manages its locks.
**Delegator**: An account that delegates some funds to an agent.

### Pallet Nomination Pools
**Pool account**: Keyless account of a pool where funds are pooled.
Members pledge their funds towards the pools. These are going to become
`Agent` accounts in `pallet-delegated-staking`.
**Pool Members**: They are individual members of the pool who
contributed funds to it. They are also `Delegator` in
`pallet-delegated-staking`.

## Changes
### Multiple Stake strategies

**TransferStake**: The current nomination pool logic can be considered a
staking strategy where delegators transfer funds to pool and stake. In
this scenario, funds are locked in pool account, and users lose the
control of their funds.

**DelegateStake**: With this PR, we introduce a new staking strategy
where individual delegators delegate fund to pool. `Delegate` implies
funds are locked in delegator account itself. Important thing to note
is, pool does not have funds of its own, but it has authorization from
its members to use these funds for staking.

We extract out all the interaction of pool with staking interface into a
new trait `StakeStrategy`. This is the logic that varies between the
above two staking strategies. We use the trait `StakeStrategy` to
implement above two strategies: `TransferStake` and `DelegateStake`.

### NominationPool
Consumes an implementation of `StakeStrategy` instead of
`StakingInterface`. I have renamed it from `Staking` to `StakeAdapter`
to clarify the difference from the earlier used trait.

To enable delegation based staking in pool, Nomination pool can be
configured as:
```
type StakeAdapter = pallet_nomination_pools::adapter::DelegateStake<Self, DelegatedStaking>;
```

Note that with the following configuration, the changes in the PR are
no-op.
```
type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>;
```

## Deployment roadmap
Plan to enable this only in Westend. In production runtimes, we can keep
pool to use `TransferStake` which will be no functional change.

Once we have a full audit, we can enable this in Kusama followed by
Polkadot.

## TODO
- [x] Runtime level (Westend) migration for existing nomination pools.
- [x] Permissionless call/ pallet::tasks for claiming delegator funds.
- [x] Add/update benches.
- [x] Migration tests.
- [x] Storage flag to mark `DelegateStake` migration and integrity
checks to not allow `TransferStake` for migrated runtimes.

---------

Signed-off-by: Matteo Muraca <mmuraca247@gmail.com>
Signed-off-by: Alexandru Gheorghe <alexandru.gheorghe@parity.io>
Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>
Signed-off-by: Adrian Catangiu <adrian@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: divdeploy <chenguangxue@outlook.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: hongkuang <liurenhong@outlook.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: gemini132 <164285545+gemini132@users.noreply.github.com>
Co-authored-by: Matteo Muraca <56828990+muraca@users.noreply.github.com>
Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com>
Co-authored-by: Alessandro Siniscalchi <asiniscalchi@gmail.com>
Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>
Co-authored-by: Ross Bulat <ross@parity.io>
Co-authored-by: Serban Iorga <serban@parity.io>
Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com>
Co-authored-by: Sam Johnson <sam@durosoft.com>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
Co-authored-by: Javier Viola <363911+pepoviola@users.noreply.github.com>
Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
Co-authored-by: Dastan <88332432+dastansam@users.noreply.github.com>
Co-authored-by: Clara van Staden <claravanstaden64@gmail.com>
Co-authored-by: Ron <yrong1997@gmail.com>
Co-authored-by: Vincent Geddes <vincent@snowfork.com>
Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>
Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
Co-authored-by: Dino Pačandi <3002868+Dinonard@users.noreply.github.com>
Co-authored-by: Andrei Eres <eresav@me.com>
Co-authored-by: Alin Dima <alin@parity.io>
Co-authored-by: Andrei Sandu <andrei-mihail@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
Co-authored-by: gupnik <nikhilgupta.iitk@gmail.com>
Co-authored-by: Vladimir Istyufeev <vladimir@parity.io>
Co-authored-by: Lulu <morgan@parity.io>
Co-authored-by: Juan Girini <juangirini@gmail.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Co-authored-by: Dónal Murray <donal.murray@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Kutsal Kaan Bilgin <kutsalbilgin@gmail.com>
Co-authored-by: Ermal Kaleci <ermalkaleci@gmail.com>
Co-authored-by: ordian <write@reusable.software>
Co-authored-by: divdeploy <166095818+divdeploy@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com>
Co-authored-by: Squirrel <gilescope@gmail.com>
Co-authored-by: HongKuang <166261675+HongKuang@users.noreply.github.com>
Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io>
Co-authored-by: Egor_P <egor@parity.io>
Co-authored-by: Aaro Altonen <48052676+altonen@users.noreply.github.com>
Co-authored-by: Dmitry Markin <dmitry@markin.tech>
Co-authored-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: Léa Narzis <78718413+lean-apple@users.noreply.github.com>
Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
Co-authored-by: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com>
Co-authored-by: command-bot <>
Co-authored-by: PG Herveou <pgherveou@gmail.com>
Co-authored-by: jimwfs <wqq1479787@163.com>
Co-authored-by: jimwfs <169986508+jimwfs@users.noreply.github.com>
Co-authored-by: polka.dom <polkadotdom@gmail.com>
  • Loading branch information
Show file tree
Hide file tree
Showing 38 changed files with 4,052 additions and 488 deletions.
30 changes: 29 additions & 1 deletion Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ members = [
"substrate/frame/nomination-pools/benchmarking",
"substrate/frame/nomination-pools/fuzzer",
"substrate/frame/nomination-pools/runtime-api",
"substrate/frame/nomination-pools/test-staking",
"substrate/frame/nomination-pools/test-delegate-stake",
"substrate/frame/nomination-pools/test-transfer-stake",
"substrate/frame/offences",
"substrate/frame/offences/benchmarking",
"substrate/frame/paged-list",
Expand Down
4 changes: 4 additions & 0 deletions polkadot/runtime/westend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pallet-society = { path = "../../../substrate/frame/society", default-features =
pallet-staking = { path = "../../../substrate/frame/staking", default-features = false }
pallet-staking-reward-curve = { package = "pallet-staking-reward-curve", path = "../../../substrate/frame/staking/reward-curve" }
pallet-staking-runtime-api = { path = "../../../substrate/frame/staking/runtime-api", default-features = false }
pallet-delegated-staking = { path = "../../../substrate/frame/delegated-staking", default-features = false }
pallet-state-trie-migration = { path = "../../../substrate/frame/state-trie-migration", default-features = false }
pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false }
pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false }
Expand Down Expand Up @@ -161,6 +162,7 @@ std = [
"pallet-beefy/std",
"pallet-collective/std",
"pallet-conviction-voting/std",
"pallet-delegated-staking/std",
"pallet-democracy/std",
"pallet-election-provider-multi-phase/std",
"pallet-election-provider-support-benchmarking?/std",
Expand Down Expand Up @@ -244,6 +246,7 @@ runtime-benchmarks = [
"pallet-balances/runtime-benchmarks",
"pallet-collective/runtime-benchmarks",
"pallet-conviction-voting/runtime-benchmarks",
"pallet-delegated-staking/runtime-benchmarks",
"pallet-democracy/runtime-benchmarks",
"pallet-election-provider-multi-phase/runtime-benchmarks",
"pallet-election-provider-support-benchmarking/runtime-benchmarks",
Expand Down Expand Up @@ -304,6 +307,7 @@ try-runtime = [
"pallet-beefy/try-runtime",
"pallet-collective/try-runtime",
"pallet-conviction-voting/try-runtime",
"pallet-delegated-staking/try-runtime",
"pallet-democracy/try-runtime",
"pallet-election-provider-multi-phase/try-runtime",
"pallet-elections-phragmen/try-runtime",
Expand Down
45 changes: 37 additions & 8 deletions polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ impl pallet_staking::Config for Runtime {
type HistoryDepth = frame_support::traits::ConstU32<84>;
type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = NominationPools;
type EventListeners = (NominationPools, DelegatedStaking);
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}
Expand Down Expand Up @@ -1360,7 +1360,8 @@ impl pallet_nomination_pools::Config for Runtime {
type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type Staking = Staking;
type StakeAdapter =
pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
type PostUnbondingPoolsWindow = ConstU32<4>;
type MaxMetadataLen = ConstU32<256>;
// we use the same number of allowed unlocking chunks as with staking.
Expand All @@ -1370,6 +1371,21 @@ impl pallet_nomination_pools::Config for Runtime {
type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
}

parameter_types! {
pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk");
pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
}

impl pallet_delegated_staking::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type PalletId = DelegatedStakingPalletId;
type Currency = Balances;
type OnSlash = ();
type SlashRewardFraction = SlashRewardFraction;
type RuntimeHoldReason = RuntimeHoldReason;
type CoreStaking = Staking;
}

impl pallet_root_testing::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
Expand Down Expand Up @@ -1518,6 +1534,10 @@ mod runtime {
#[runtime::pallet_index(37)]
pub type Treasury = pallet_treasury;

// Staking extension for delegation
#[runtime::pallet_index(38)]
pub type DelegatedStaking = pallet_delegated_staking;

// Parachains pallets. Start indices at 40 to leave room.
#[runtime::pallet_index(41)]
pub type ParachainsOrigin = parachains_origin;
Expand Down Expand Up @@ -1621,11 +1641,12 @@ pub type SignedExtra = (
frame_metadata_hash_extension::CheckMetadataHash<Runtime>,
);

pub struct NominationPoolsMigrationV4OldPallet;
impl Get<Perbill> for NominationPoolsMigrationV4OldPallet {
fn get() -> Perbill {
Perbill::from_percent(100)
}
parameter_types! {
// This is the max pools that will be migrated in the runtime upgrade. Westend has more pools
// than this, but we want to emulate some non migrated pools. In prod runtimes, if weight is not
// a concern, it is recommended to set to (existing pools + 10) to also account for any new
// pools getting created before the migration is actually executed.
pub const MaxPoolsToMigrate: u32 = 250;
}

/// All migrations that will run on the next runtime upgrade.
Expand Down Expand Up @@ -1658,7 +1679,15 @@ pub mod migrations {
}

/// Unreleased migrations. Add new ones here:
pub type Unreleased = (pallet_staking::migrations::v15::MigrateV14ToV15<Runtime>,);
pub type Unreleased = (
// Migrate NominationPools to `DelegateStake` adapter. This is unversioned upgrade and
// should not be applied yet in Kusama/Polkadot.
pallet_nomination_pools::migration::unversioned::DelegationStakeMigration<
Runtime,
MaxPoolsToMigrate,
>,
pallet_staking::migrations::v15::MigrateV14ToV15<Runtime>,
);
}

/// Unchecked extrinsic type as expected by this runtime.
Expand Down
Loading

0 comments on commit 8949856

Please sign in to comment.