Nomination Pools (#10694)

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Add admin roles and make some calls permissionless

* Destroy pool in withdraw unbonded

* Add docs on pool admin

* Fixup tests

* Test unbond_other permissionless scenarios

* Test withdraw unbonded permissionless

* Test only can join open pools

* Move unsafe set state to mock

* Test: nominate_works

* Add bounds: MinJoinBond, MinCreateBond, MaxPools

* Test MinCreateBond, MinJoinBond, MaxPools

* Add post checks to tests

* Remove some TODOs

* Setup weight infrastructure

* Benchmark claim_payout

* Benchmark create

* Benchmark nominate

* Benchmark join

* Benchmark unbond_other

* Refactor join benchmark to use scenario setup

* Clean up and address warnings

* Basic withdraw unbonded benchmarks

* Refactor nominate benchmark

* Refactor claim payout

* Add feature sp-staking/runtime-benchmarks

* Get node runtime to compile

* Get node to run

* Make claim_payout bench work with node

* Make pool_withdraw_unbonded bench work with node

* Make withdraw_unbonded_other work with node runtime'

* Make create benchmark work with node

* Make nominate benchmark work with node runtime

* WiP new benchmark crate

* Implement initial mock for benchmarks

* Establish benchmark setup logic

* Get claim payout and nominate benchmarks working

* Remove pool bench utils; make struct fields pub insteaad

* Get more benchmarks to work; trim interface trait

* Some more top level docs

* Finish tranistion benchmarks to crate

* Hook up benchmark pallet to node runtime

* Get benches to work with node runtime

* cargo run --quiet --profile=production  --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_nomination_pools --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/nomination-pools/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Benchmark withdraw_unbonded_other_kill

* Delete old benchmarking files

* Refunds for withdraw_unbonded

* Remove some TODOs

* 'Don't return an option for the current_era'

* Streamline extrinsic docs

* small docs tweaks

* Refactor ledger::slash

* Add on_slash impl for nomination pools

* slash refactor wip

* WIP slash working

* DRY Ledger::stash

* Fix slash saturation

* Remove unused param from slash

* Docs and warnings

* Test ledger::slash

* save progress

* Introduce counter for delegators

* Add tests for max delegator errors

* Reproducible account ids

* Adapt tests to new account id format

* Simplify create_accounts api

* Fix staking tests

* Save PerBill slash impl before removing

* Rever ledger slash test

* Get node runtime to work

* Organize sub pools by unbond era, not curren era

* staking: Proportional ledger slashing

* Some comment cleanup

* Add more test post checks

* Update frame/staking/src/pallet/mod.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Tests: account for storing unbond era

* Improve docs for staking interface

* Add events Created, Destroyed

* withdraw_unbonded: Remove useless withdraw dust check

* Test: withdraw_unbonded_other_handles_faulty_sub_pool_accounting

* Add extrinsics: set_state_other, set_metadata

* Test: set_state_other_works

* Test: set_metadata_works

* Add benchmarks for set_state_other, set_metadata

* Fix benchmarks

* Add weight info for new extrinsics

* Some feedback

* duo feedback

* Incorporate some more feedback

* integrate more kian feedback

* integrate more kian feedback

* More improvements

* Add destroying_mul

* Make do_reward_payout take refs

* Remove some TODOs

* Add test for saturating

* feedback

* Fix join test

* use `inner` for nested types in nomination pools (#11030)

* Use nested inner type for pool

* make tests and benchmarks work

* remove feat

* all tests work now

* fix node-runtime

* nomination-pools: update benches for new account format (#11033)

* Update benches to new account format

* More sensible seeds

* bring back rward account sanity check

* Comment

* Add extrinsic set_configs (#11038)

* Better sanity checks for nomination pools  (#11042)

* new sanity checks, few other changes

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* nomination-pools: Fix state event regression + benches (#11045)

* new sanity checks, few other changes

* Fix benches, improve sanity check

* Remove useless clear storage in benchmarking

* Set state

* Save

* Doc

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: kianenigma <kian@parity.io>

* FMT

* Try fill in all staking configs

* Fix build

* More changes to nomination pools (#11050)

* new sanity checks, few other changes

* some last touches as a whole

* Apply suggestions from code review

* Remove redundant event

* Improve unbond_other error handling

* Remove comment

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>
Co-authored-by: emostov <32168567+emostov@users.noreply.github.com>

* Remove sanity module and some TODOs

* round of feedback and imp from kian

* Add TODO for ED QoL at reward pool creation

* Make sure reward pool never gets dusted

* Improve error type

* demonstrate per_thing usage

* Update sanity check & fix create_works

* Improve test ext pool creation & fix some more tests

* Try revert

* Revert "Try revert"

This reverts commit c044c94730e1a370eecd8f5b2c4f632835913063.

* Revert "Improve test ext pool creation & fix some more tests"

This reverts commit 1e862a64a7423479260c6e5ad1bd4c8c95651f3a.

* Revert "Update sanity check & fix create_works"

This reverts commit 568a7b727687e4d585e2796afc638df97b83c632.

Roll back reward account funding

* Revert "Improve error type"

This reverts commit 4b993ee601a037e7a44e4a49bbfd60cf45b38b78.

* Revert "Make sure reward pool never gets dusted"

This reverts commit e7a3eb45bdfd156d3f6d94d194e988032ebbc593.

revert

* Update some tests

* FMT

* Test that era offset works correctly

* Update mocks

* Remove unnescary docs

* Doc updates

* Update calculate_delegator_payout_works_with_a_pool_of_1

* Fix test: claim_payout_works

* do_reward_payout_correctly_sets_pool_state_to_destroying

* Remove test do_reward_payout_errors_correctly

* Fix test: do_reward_payout_works

* Fix test: create_errors_correctly

* Fix test: create works

* Fix test: unbond_other_of_3_works

* Ensure that ED is transferred into reward pool upon creation

* WIP pool lifecycle test

* Fix benchmarks

* Add sanity check for ED + reward pools

* `bond_extra` for nomination pools (#11100)

* bond_extra for nomination pools

* Update frame/nomination-pools/src/lib.rs

* Update frame/nomination-pools/src/lib.rs

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* add benchmarks

* remove the min logic of bond_extra

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* FMT

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* make it compile end to end

* Update some type viz

* Update kick terminology

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/nomination-pools/src/lib.rs

* Cache bonded account when creating pool

* Add bond extra weight stuff

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update docs for pool withdraw unbonded

* Update docs for unbond

* Improve Doc

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/nomination-pools/Cargo.toml

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Improve Docs

* Some docs improvements

* fmt

* Remove unlock_era

* Fix accidental frame-support regression

* Fix issue with transactions in tests

* Fix doc links

* Make sure result in test is used

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fix can toggle state

* Account for new_funds in ok to be open

* Update docs: ok_to_withdraw_unbonded_other_with

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Remove some staking comments

* Rename SubPoolsWithEra to UnbondingPoolsWithEra

* Use validators length for benchmarks

* Use metadata length for benchmarks

* Remove debug assert eq

* docs

* Fix test: withdraw_unbonded_other_errors_correctly

* Fix check for having enough balance to create the pool

* Bond event for pool creation

* Ok to be open

* FMT

* Remove _other postfix

* Update frame/staking/src/lib.rs

* Adjust tests to account for only remove when < ED

* Remove stale TODOs

* Remove dupe test

* Fix build

* Make sure to convert to u256 so we don't saturate

* Refund depositor with reward pool fee

* FMT

* Remove reachable defensive

* Use compact encoding for relevant extrinsics

* Remove unnescary make_free_be for cleaning reward account

* Add not to maintainers for reward account accounting

* Remove note to maintainers from public doc

* Make sure all configs have currency balance

* Avoid saturation in balance_to_unbond

* Partial Unbonding for Nomination Pools (#11212)

* first draft of partial unbonding for pools

* remove option

* Add some more tests and fix issues

* Fix all tests

* simplify some tests

* Update frame/nomination-pools/src/mock.rs

* remove clone

* rename to delegator_unbonding_eras

* Update frame/nomination-pools/src/tests.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update frame/nomination-pools/src/tests.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update frame/nomination-pools/src/tests.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* remove pub

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* undo

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* leftovers

* fix invariant

* Fix the depositor assumption

* round of self-review

* little bit more cleanup

* Update frame/nomination-pools/src/mock.rs

* Apply suggestions from code review

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Fix interpretation of MinCreateBond

* controvesial refactor

* rename

* make everything build

* add TODO about killing the reward account

* Update frame/nomination-pools/src/lib.rs

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update frame/nomination-pools/src/lib.rs

* last self-review

Co-authored-by: Zeke Mostov <z.mostov@gmail.com>

* Update Cargo.lock

* Rename Delegator to PoolMember

* fmt

* Get runtime to build with runtime-benchmarks feature

* Update Cargo.lock

* Fix asserts to work in more scenarios

* gte not gt

* cargo run --quiet --profile=production  --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_nomination_pools --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/nomination-pools/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Update frame/staking/src/mock.rs

* Update frame/nomination-pools/src/lib.rs

* Update frame/staking/src/slashing.rs

* Apply suggestions from code review

* fmt

* Fix some tests

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: kianenigma <kian@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Zeke Mostov
2022-04-27 03:46:47 -07:00
committed by GitHub
parent bdf55aab19
commit 247f33dc0b
27 changed files with 7174 additions and 15 deletions
+42
View File
@@ -5027,6 +5027,8 @@ dependencies = [
"pallet-membership",
"pallet-mmr",
"pallet-multisig",
"pallet-nomination-pools",
"pallet-nomination-pools-benchmarking",
"pallet-offences",
"pallet-offences-benchmarking",
"pallet-preimage",
@@ -6182,6 +6184,46 @@ dependencies = [
"sp-std",
]
[[package]]
name = "pallet-nomination-pools"
version = "1.0.0"
dependencies = [
"frame-support",
"frame-system",
"pallet-balances",
"parity-scale-codec",
"scale-info",
"sp-core",
"sp-io",
"sp-runtime",
"sp-staking",
"sp-std",
"sp-tracing",
]
[[package]]
name = "pallet-nomination-pools-benchmarking"
version = "1.0.0"
dependencies = [
"frame-benchmarking",
"frame-election-provider-support",
"frame-support",
"frame-system",
"pallet-bags-list",
"pallet-balances",
"pallet-nomination-pools",
"pallet-staking",
"pallet-staking-reward-curve",
"pallet-timestamp",
"parity-scale-codec",
"scale-info",
"sp-core",
"sp-io",
"sp-runtime",
"sp-staking",
"sp-std",
]
[[package]]
name = "pallet-offences"
version = "4.0.0-dev"
+2
View File
@@ -109,6 +109,8 @@ members = [
"frame/offences",
"frame/preimage",
"frame/proxy",
"frame/nomination-pools",
"frame/nomination-pools/benchmarking",
"frame/randomness-collective-flip",
"frame/recovery",
"frame/referenda",
+1
View File
@@ -363,6 +363,7 @@ pub fn testnet_genesis(
gilt: Default::default(),
transaction_storage: Default::default(),
transaction_payment: Default::default(),
nomination_pools: Default::default(),
}
}
+4
View File
@@ -76,6 +76,8 @@ pallet-lottery = { version = "4.0.0-dev", default-features = false, path = "../.
pallet-membership = { version = "4.0.0-dev", default-features = false, path = "../../../frame/membership" }
pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../../frame/merkle-mountain-range" }
pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" }
pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"}
pallet-nomination-pools-benchmarking = { version = "1.0.0", default-features = false, optional = true, path = "../../../frame/nomination-pools/benchmarking" }
pallet-offences = { version = "4.0.0-dev", default-features = false, path = "../../../frame/offences" }
pallet-offences-benchmarking = { version = "4.0.0-dev", path = "../../../frame/offences/benchmarking", default-features = false, optional = true }
pallet-preimage = { version = "4.0.0-dev", default-features = false, path = "../../../frame/preimage" }
@@ -140,6 +142,7 @@ std = [
"pallet-membership/std",
"pallet-mmr/std",
"pallet-multisig/std",
"pallet-nomination-pools/std",
"pallet-identity/std",
"pallet-scheduler/std",
"node-primitives/std",
@@ -210,6 +213,7 @@ runtime-benchmarks = [
"pallet-membership/runtime-benchmarks",
"pallet-mmr/runtime-benchmarks",
"pallet-multisig/runtime-benchmarks",
"pallet-nomination-pools-benchmarking",
"pallet-offences-benchmarking",
"pallet-preimage/runtime-benchmarks",
"pallet-proxy/runtime-benchmarks",
+38 -1
View File
@@ -561,7 +561,7 @@ impl pallet_staking::Config for Runtime {
type GenesisElectionProvider = onchain::UnboundedExecution<OnChainSeqPhragmen>;
type VoterList = BagsList;
type MaxUnlockingChunks = ConstU32<32>;
type OnStakerSlash = ();
type OnStakerSlash = NominationPools;
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
type BenchmarkingConfig = StakingBenchmarkingConfig;
}
@@ -705,6 +705,38 @@ impl pallet_bags_list::Config for Runtime {
type Score = VoteWeight;
}
parameter_types! {
pub const PostUnbondPoolsWindow: u32 = 4;
pub const NominationPoolsPalletId: PalletId = PalletId(*b"py/npols");
}
use sp_runtime::traits::Convert;
pub struct BalanceToU256;
impl Convert<Balance, sp_core::U256> for BalanceToU256 {
fn convert(balance: Balance) -> sp_core::U256 {
sp_core::U256::from(balance)
}
}
pub struct U256ToBalance;
impl Convert<sp_core::U256, Balance> for U256ToBalance {
fn convert(n: sp_core::U256) -> Balance {
n.try_into().unwrap_or(Balance::max_value())
}
}
impl pallet_nomination_pools::Config for Runtime {
type WeightInfo = ();
type Event = Event;
type Currency = Balances;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = pallet_staking::Pallet<Self>;
type PostUnbondingPoolsWindow = PostUnbondPoolsWindow;
type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>;
type PalletId = NominationPoolsPalletId;
}
parameter_types! {
pub const VoteLockingPeriod: BlockNumber = 30 * DAYS;
}
@@ -1470,6 +1502,7 @@ construct_runtime!(
Remark: pallet_remark,
ConvictionVoting: pallet_conviction_voting,
Whitelist: pallet_whitelist,
NominationPools: pallet_nomination_pools,
}
);
@@ -1554,6 +1587,7 @@ mod benches {
[pallet_membership, TechnicalMembership]
[pallet_mmr, Mmr]
[pallet_multisig, Multisig]
[pallet_nomination_pools, NominationPoolsBench::<Runtime>]
[pallet_offences, OffencesBench::<Runtime>]
[pallet_preimage, Preimage]
[pallet_proxy, Proxy]
@@ -1869,6 +1903,7 @@ impl_runtime_apis! {
use pallet_election_provider_support_benchmarking::Pallet as EPSBench;
use frame_system_benchmarking::Pallet as SystemBench;
use baseline::Pallet as BaselineBench;
use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench;
let mut list = Vec::<BenchmarkList>::new();
list_benchmarks!(list, extra);
@@ -1891,12 +1926,14 @@ impl_runtime_apis! {
use pallet_election_provider_support_benchmarking::Pallet as EPSBench;
use frame_system_benchmarking::Pallet as SystemBench;
use baseline::Pallet as BaselineBench;
use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench;
impl pallet_session_benchmarking::Config for Runtime {}
impl pallet_offences_benchmarking::Config for Runtime {}
impl pallet_election_provider_support_benchmarking::Config for Runtime {}
impl frame_system_benchmarking::Config for Runtime {}
impl baseline::Config for Runtime {}
impl pallet_nomination_pools_benchmarking::Config for Runtime {}
let whitelist: Vec<TrackedStorageKey> = vec![
// Block Number
@@ -92,5 +92,6 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec<AccountId>) -> Gen
gilt: Default::default(),
transaction_storage: Default::default(),
transaction_payment: Default::default(),
nomination_pools: Default::default(),
}
}
+6 -6
View File
@@ -179,10 +179,10 @@ frame_benchmarking::benchmarks! {
vec![heavier, lighter, heavier_prev, heavier_next]
)
}
}
frame_benchmarking::impl_benchmark_test_suite!(
Pallet,
crate::mock::ExtBuilder::default().skip_genesis_ids().build(),
crate::mock::Runtime
);
impl_benchmark_test_suite!(
Pallet,
crate::mock::ExtBuilder::default().skip_genesis_ids().build(),
crate::mock::Runtime
);
}
@@ -0,0 +1,44 @@
[package]
name = "pallet-nomination-pools"
version = "1.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME nomination pools pallet"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
# parity
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
# FRAME
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
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-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" }
sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" }
[dev-dependencies]
pallet-balances = { version = "4.0.0-dev", path = "../balances" }
sp-io = { version = "6.0.0", path = "../../primitives/io" }
sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" }
[features]
runtime-benchmarks = []
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"frame-support/std",
"frame-system/std",
"sp-runtime/std",
"sp-std/std",
"sp-staking/std",
"sp-core/std",
]
@@ -0,0 +1,55 @@
[package]
name = "pallet-nomination-pools-benchmarking"
version = "1.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME nomination pools pallet benchmarking"
readme = "README.md"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
# parity
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
# FRAME
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" }
frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../../election-provider-support" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" }
pallet-bags-list = { version = "4.0.0-dev", default-features = false, features = ["runtime-benchmarks"], path = "../../bags-list" }
pallet-staking = { version = "4.0.0-dev", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" }
pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../", features = ["runtime-benchmarks"] }
# Substrate Primitives
sp-runtime = { version = "6.0.0", default-features = false, path = "../../../primitives/runtime" }
sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/staking" }
sp-std = { version = "4.0.0", default-features = false, path = "../../../primitives/std" }
[dev-dependencies]
pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../balances" }
pallet-timestamp = { version = "4.0.0-dev", path = "../../timestamp" }
pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../../staking/reward-curve" }
sp-core = { version = "6.0.0", path = "../../../primitives/core" }
sp-io = { version = "6.0.0", path = "../../../primitives/io" }
[features]
default = ["std"]
std = [
"frame-benchmarking/std",
"frame-election-provider-support/std",
"frame-support/std",
"frame-system/std",
"pallet-bags-list/std",
"pallet-staking/std",
"pallet-nomination-pools/std",
"sp-runtime/std",
"sp-staking/std",
"sp-std/std",
"pallet-balances/std",
]
@@ -0,0 +1,639 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Benchmarks for the nomination pools coupled with the staking and bags list pallets.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;
use frame_benchmarking::{account, frame_support::traits::Currency, vec, whitelist_account, Vec};
use frame_election_provider_support::SortedListProvider;
use frame_support::{ensure, traits::Get};
use frame_system::RawOrigin as Origin;
use pallet_nomination_pools::{
BalanceOf, BondExtra, BondedPoolInner, BondedPools, ConfigOp, MaxPoolMembers,
MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools,
PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
};
use sp_runtime::traits::{Bounded, Zero};
use sp_staking::{EraIndex, StakingInterface};
// `frame_benchmarking::benchmarks!` macro needs this
use pallet_nomination_pools::Call;
type CurrencyOf<T> = <T as pallet_nomination_pools::Config>::Currency;
const USER_SEED: u32 = 0;
const MAX_SPANS: u32 = 100;
pub trait Config:
pallet_nomination_pools::Config + pallet_staking::Config + pallet_bags_list::Config
{
}
pub struct Pallet<T: Config>(Pools<T>);
fn create_funded_user_with_balance<T: pallet_nomination_pools::Config>(
string: &'static str,
n: u32,
balance: BalanceOf<T>,
) -> T::AccountId {
let user = account(string, n, USER_SEED);
T::Currency::make_free_balance_be(&user, balance);
user
}
// Create a bonded pool account, bonding `balance` and giving the account `balance * 2` free
// balance.
fn create_pool_account<T: pallet_nomination_pools::Config>(
n: u32,
balance: BalanceOf<T>,
) -> (T::AccountId, T::AccountId) {
let ed = CurrencyOf::<T>::minimum_balance();
let pool_creator: T::AccountId =
create_funded_user_with_balance::<T>("pool_creator", n, ed + balance * 2u32.into());
Pools::<T>::create(
Origin::Signed(pool_creator.clone()).into(),
balance,
pool_creator.clone(),
pool_creator.clone(),
pool_creator.clone(),
)
.unwrap();
let pool_account = pallet_nomination_pools::BondedPools::<T>::iter()
.find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator)
.map(|(pool_id, _)| Pools::<T>::create_bonded_account(pool_id))
.expect("pool_creator created a pool above");
(pool_creator, pool_account)
}
fn vote_to_balance<T: pallet_nomination_pools::Config>(
vote: u64,
) -> Result<BalanceOf<T>, &'static str> {
vote.try_into().map_err(|_| "could not convert u64 to Balance")
}
#[allow(unused)]
struct ListScenario<T: pallet_nomination_pools::Config> {
/// Stash/Controller that is expected to be moved.
origin1: T::AccountId,
creator1: T::AccountId,
dest_weight: BalanceOf<T>,
origin1_member: Option<T::AccountId>,
}
impl<T: Config> ListScenario<T> {
/// An expensive scenario for bags-list implementation:
///
/// - the node to be updated (r) is the head of a bag that has at least one other node. The bag
/// itself will need to be read and written to update its head. The node pointed to by r.next
/// will need to be read and written as it will need to have its prev pointer updated. Note
/// that there are two other worst case scenarios for bag removal: 1) the node is a tail and
/// 2) the node is a middle node with prev and next; all scenarios end up with the same number
/// of storage reads and writes.
///
/// - the destination bag has at least one node, which will need its next pointer updated.
pub(crate) fn new(
origin_weight: BalanceOf<T>,
is_increase: bool,
) -> Result<Self, &'static str> {
ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0");
ensure!(
pallet_nomination_pools::MaxPools::<T>::get().unwrap_or(0) >= 3,
"must allow at least three pools for benchmarks"
);
// Burn the entire issuance.
let i = CurrencyOf::<T>::burn(CurrencyOf::<T>::total_issuance());
sp_std::mem::forget(i);
// Create accounts with the origin weight
let (pool_creator1, pool_origin1) = create_pool_account::<T>(USER_SEED + 1, origin_weight);
T::StakingInterface::nominate(
pool_origin1.clone(),
// NOTE: these don't really need to be validators.
vec![account("random_validator", 0, USER_SEED)],
)?;
let (_, pool_origin2) = create_pool_account::<T>(USER_SEED + 2, origin_weight);
T::StakingInterface::nominate(
pool_origin2.clone(),
vec![account("random_validator", 0, USER_SEED)].clone(),
)?;
// Find a destination weight that will trigger the worst case scenario
let dest_weight_as_vote = <T as pallet_staking::Config>::VoterList::score_update_worst_case(
&pool_origin1,
is_increase,
);
let dest_weight: BalanceOf<T> =
dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?;
// Create an account with the worst case destination weight
let (_, pool_dest1) = create_pool_account::<T>(USER_SEED + 3, dest_weight);
T::StakingInterface::nominate(
pool_dest1.clone(),
vec![account("random_validator", 0, USER_SEED)],
)?;
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_origin2)).unwrap(), origin_weight);
assert_eq!(vote_to_balance::<T>(weight_of(&pool_dest1)).unwrap(), dest_weight);
Ok(ListScenario {
origin1: pool_origin1,
creator1: pool_creator1,
dest_weight,
origin1_member: None,
})
}
fn add_joiner(mut self, amount: BalanceOf<T>) -> Self {
let amount = MinJoinBond::<T>::get()
.max(CurrencyOf::<T>::minimum_balance())
// Max `amount` with minimum thresholds for account balance and joining a pool
// to ensure 1) the user can be created and 2) can join the pool
.max(amount);
let joiner: T::AccountId = account("joiner", USER_SEED, 0);
self.origin1_member = Some(joiner.clone());
CurrencyOf::<T>::make_free_balance_be(&joiner, amount * 2u32.into());
let original_bonded = T::StakingInterface::active_stake(&self.origin1).unwrap();
// Unbond `amount` from the underlying pool account so when the member joins
// we will maintain `current_bonded`.
T::StakingInterface::unbond(self.origin1.clone(), amount)
.expect("the pool was created in `Self::new`.");
// Account pool points for the unbonded balance.
BondedPools::<T>::mutate(&1, |maybe_pool| {
maybe_pool.as_mut().map(|pool| pool.points -= amount)
});
Pools::<T>::join(Origin::Signed(joiner.clone()).into(), amount, 1).unwrap();
// Sanity check that the vote weight is still the same as the original bonded
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
assert_eq!(vote_to_balance::<T>(weight_of(&self.origin1)).unwrap(), original_bonded);
// Sanity check the member was added correctly
let member = PoolMembers::<T>::get(&joiner).unwrap();
assert_eq!(member.points, amount);
assert_eq!(member.pool_id, 1);
self
}
}
frame_benchmarking::benchmarks! {
join {
let origin_weight = pallet_nomination_pools::MinCreateBond::<T>::get()
.max(CurrencyOf::<T>::minimum_balance())
* 2u32.into();
// setup the worst case list scenario.
let scenario = ListScenario::<T>::new(origin_weight, true)?;
assert_eq!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap(),
origin_weight
);
let max_additional = scenario.dest_weight.clone() - origin_weight;
let joiner_free = CurrencyOf::<T>::minimum_balance() + max_additional;
let joiner: T::AccountId
= create_funded_user_with_balance::<T>("joiner", 0, joiner_free);
whitelist_account!(joiner);
}: _(Origin::Signed(joiner.clone()), max_additional, 1)
verify {
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), joiner_free - max_additional);
assert_eq!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap(),
scenario.dest_weight
);
}
bond_extra_transfer {
let origin_weight = pallet_nomination_pools::MinCreateBond::<T>::get()
.max(CurrencyOf::<T>::minimum_balance())
* 2u32.into();
let scenario = ListScenario::<T>::new(origin_weight, true)?;
let extra = scenario.dest_weight.clone() - origin_weight;
// creator of the src pool will bond-extra, bumping itself to dest bag.
}: bond_extra(Origin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra))
verify {
assert!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap() >=
scenario.dest_weight
);
}
bond_extra_reward {
let origin_weight = pallet_nomination_pools::MinCreateBond::<T>::get()
.max(CurrencyOf::<T>::minimum_balance())
* 2u32.into();
let scenario = ListScenario::<T>::new(origin_weight, true)?;
let extra = (scenario.dest_weight.clone() - origin_weight).max(CurrencyOf::<T>::minimum_balance());
// transfer exactly `extra` to the depositor of the src pool (1),
let reward_account1 = Pools::<T>::create_reward_account(1);
assert!(extra >= CurrencyOf::<T>::minimum_balance());
CurrencyOf::<T>::deposit_creating(&reward_account1, extra);
}: bond_extra(Origin::Signed(scenario.creator1.clone()), BondExtra::Rewards)
verify {
assert!(
T::StakingInterface::active_stake(&scenario.origin1).unwrap() >=
scenario.dest_weight
);
}
claim_payout {
let origin_weight = pallet_nomination_pools::MinCreateBond::<T>::get().max(CurrencyOf::<T>::minimum_balance()) * 2u32.into();
let ed = CurrencyOf::<T>::minimum_balance();
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight);
let reward_account = Pools::<T>::create_reward_account(1);
// Send funds to the reward account of the pool
CurrencyOf::<T>::make_free_balance_be(&reward_account, ed + origin_weight);
// Sanity check
assert_eq!(
CurrencyOf::<T>::free_balance(&depositor),
origin_weight
);
whitelist_account!(depositor);
}:_(Origin::Signed(depositor.clone()))
verify {
assert_eq!(
CurrencyOf::<T>::free_balance(&depositor),
origin_weight * 2u32.into()
);
assert_eq!(
CurrencyOf::<T>::free_balance(&reward_account),
ed + Zero::zero()
);
}
unbond {
// The weight the nominator will start at. The value used here is expected to be
// significantly higher than the first position in a list (e.g. the first bag threshold).
let origin_weight = BalanceOf::<T>::try_from(952_994_955_240_703u128)
.map_err(|_| "balance expected to be a u128")
.unwrap();
let scenario = ListScenario::<T>::new(origin_weight, false)?;
let amount = origin_weight - scenario.dest_weight.clone();
let scenario = scenario.add_joiner(amount);
let member_id = scenario.origin1_member.unwrap().clone();
let all_points = PoolMembers::<T>::get(&member_id).unwrap().points;
whitelist_account!(member_id);
}: _(Origin::Signed(member_id.clone()), member_id.clone(), all_points)
verify {
let bonded_after = T::StakingInterface::active_stake(&scenario.origin1).unwrap();
// We at least went down to the destination bag
assert!(bonded_after <= scenario.dest_weight.clone());
let member = PoolMembers::<T>::get(
&member_id
)
.unwrap();
assert_eq!(
member.unbonding_eras.keys().cloned().collect::<Vec<_>>(),
vec![0 + T::StakingInterface::bonding_duration()]
);
assert_eq!(
member.unbonding_eras.values().cloned().collect::<Vec<_>>(),
vec![all_points]
);
}
pool_withdraw_unbonded {
let s in 0 .. MAX_SPANS;
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
// Add a new member
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
Pools::<T>::join(Origin::Signed(joiner.clone()).into(), min_join_bond, 1)
.unwrap();
// Sanity check join worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
min_create_bond + min_join_bond
);
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), min_join_bond);
// Unbond the new member
Pools::<T>::fully_unbond(Origin::Signed(joiner.clone()).into(), joiner.clone()).unwrap();
// Sanity check that unbond worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
min_create_bond
);
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
// Set the current era
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
// Add `s` count of slashing spans to storage.
pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
whitelist_account!(pool_account);
}: _(Origin::Signed(pool_account.clone()), 1, s)
verify {
// The joiners funds didn't change
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), min_join_bond);
// The unlocking chunk was removed
assert_eq!(pallet_staking::Ledger::<T>::get(pool_account).unwrap().unlocking.len(), 0);
}
withdraw_unbonded_update {
let s in 0 .. MAX_SPANS;
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
// Add a new member
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
Pools::<T>::join(Origin::Signed(joiner.clone()).into(), min_join_bond, 1)
.unwrap();
// Sanity check join worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
min_create_bond + min_join_bond
);
assert_eq!(CurrencyOf::<T>::free_balance(&joiner), min_join_bond);
// Unbond the new member
pallet_staking::CurrentEra::<T>::put(0);
Pools::<T>::fully_unbond(Origin::Signed(joiner.clone()).into(), joiner.clone()).unwrap();
// Sanity check that unbond worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
min_create_bond
);
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
// Set the current era to ensure we can withdraw unbonded funds
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
whitelist_account!(joiner);
}: withdraw_unbonded(Origin::Signed(joiner.clone()), joiner.clone(), s)
verify {
assert_eq!(
CurrencyOf::<T>::free_balance(&joiner),
min_join_bond * 2u32.into()
);
// The unlocking chunk was removed
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 0);
}
withdraw_unbonded_kill {
let s in 0 .. MAX_SPANS;
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
// We set the pool to the destroying state so the depositor can leave
BondedPools::<T>::try_mutate(&1, |maybe_bonded_pool| {
maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
bonded_pool.state = PoolState::Destroying;
})
})
.unwrap();
// Unbond the creator
pallet_staking::CurrentEra::<T>::put(0);
// Simulate some rewards so we can check if the rewards storage is cleaned up. We check this
// here to ensure the complete flow for destroying a pool works - the reward pool account
// should never exist by time the depositor withdraws so we test that it gets cleaned
// up when unbonding.
let reward_account = Pools::<T>::create_reward_account(1);
assert!(frame_system::Account::<T>::contains_key(&reward_account));
Pools::<T>::fully_unbond(Origin::Signed(depositor.clone()).into(), depositor.clone()).unwrap();
// Sanity check that unbond worked
assert_eq!(
T::StakingInterface::active_stake(&pool_account).unwrap(),
Zero::zero()
);
assert_eq!(
CurrencyOf::<T>::free_balance(&pool_account),
min_create_bond
);
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
// Set the current era to ensure we can withdraw unbonded funds
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
// Some last checks that storage items we expect to get cleaned up are present
assert!(pallet_staking::Ledger::<T>::contains_key(&pool_account));
assert!(BondedPools::<T>::contains_key(&1));
assert!(SubPoolsStorage::<T>::contains_key(&1));
assert!(RewardPools::<T>::contains_key(&1));
assert!(PoolMembers::<T>::contains_key(&depositor));
assert!(frame_system::Account::<T>::contains_key(&reward_account));
whitelist_account!(depositor);
}: withdraw_unbonded(Origin::Signed(depositor.clone()), depositor.clone(), s)
verify {
// Pool removal worked
assert!(!pallet_staking::Ledger::<T>::contains_key(&pool_account));
assert!(!BondedPools::<T>::contains_key(&1));
assert!(!SubPoolsStorage::<T>::contains_key(&1));
assert!(!RewardPools::<T>::contains_key(&1));
assert!(!PoolMembers::<T>::contains_key(&depositor));
assert!(!frame_system::Account::<T>::contains_key(&pool_account));
assert!(!frame_system::Account::<T>::contains_key(&reward_account));
// Funds where transferred back correctly
assert_eq!(
CurrencyOf::<T>::free_balance(&depositor),
// gets bond back + rewards collecting when unbonding
min_create_bond * 2u32.into() + CurrencyOf::<T>::minimum_balance()
);
}
create {
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let depositor: T::AccountId = account("depositor", USER_SEED, 0);
// Give the depositor some balance to bond
CurrencyOf::<T>::make_free_balance_be(&depositor, min_create_bond * 2u32.into());
// Make sure no pools exist as a pre-condition for our verify checks
assert_eq!(RewardPools::<T>::count(), 0);
assert_eq!(BondedPools::<T>::count(), 0);
whitelist_account!(depositor);
}: _(
Origin::Signed(depositor.clone()),
min_create_bond,
depositor.clone(),
depositor.clone(),
depositor.clone()
)
verify {
assert_eq!(RewardPools::<T>::count(), 1);
assert_eq!(BondedPools::<T>::count(), 1);
let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
assert_eq!(
new_pool,
BondedPoolInner {
points: min_create_bond,
state: PoolState::Open,
member_counter: 1,
roles: PoolRoles {
depositor: depositor.clone(),
root: depositor.clone(),
nominator: depositor.clone(),
state_toggler: depositor.clone(),
},
}
);
assert_eq!(
T::StakingInterface::active_stake(&Pools::<T>::create_bonded_account(1)),
Some(min_create_bond)
);
}
nominate {
let n in 1 .. T::MaxNominations::get();
// Create a pool
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
// Create some accounts to nominate. For the sake of benchmarking they don't need to be
// actual validators
let validators: Vec<_> = (0..n)
.map(|i| account("stash", USER_SEED, i))
.collect();
whitelist_account!(depositor);
}:_(Origin::Signed(depositor.clone()), 1, validators)
verify {
assert_eq!(RewardPools::<T>::count(), 1);
assert_eq!(BondedPools::<T>::count(), 1);
let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
assert_eq!(
new_pool,
BondedPoolInner {
points: min_create_bond,
state: PoolState::Open,
member_counter: 1,
roles: PoolRoles {
depositor: depositor.clone(),
root: depositor.clone(),
nominator: depositor.clone(),
state_toggler: depositor.clone(),
}
}
);
assert_eq!(
T::StakingInterface::active_stake(&Pools::<T>::create_bonded_account(1)),
Some(min_create_bond)
);
}
set_state {
// Create a pool
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
BondedPools::<T>::mutate(&1, |maybe_pool| {
// Force the pool into an invalid state
maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into());
});
let caller = account("caller", 0, USER_SEED);
whitelist_account!(caller);
}:_(Origin::Signed(caller), 1, PoolState::Destroying)
verify {
assert_eq!(BondedPools::<T>::get(1).unwrap().state, PoolState::Destroying);
}
set_metadata {
let n in 1 .. <T as pallet_nomination_pools::Config>::MaxMetadataLen::get();
// Create a pool
let min_create_bond = MinCreateBond::<T>::get()
.max(T::StakingInterface::minimum_bond())
.max(CurrencyOf::<T>::minimum_balance());
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
// Create metadata of the max possible size
let metadata: Vec<u8> = (0..n).map(|_| 42).collect();
whitelist_account!(depositor);
}:_(Origin::Signed(depositor), 1, metadata.clone())
verify {
assert_eq!(Metadata::<T>::get(&1), metadata);
}
set_configs {
}:_(
Origin::Root,
ConfigOp::Set(BalanceOf::<T>::max_value()),
ConfigOp::Set(BalanceOf::<T>::max_value()),
ConfigOp::Set(u32::MAX),
ConfigOp::Set(u32::MAX),
ConfigOp::Set(u32::MAX)
) verify {
assert_eq!(MinJoinBond::<T>::get(), BalanceOf::<T>::max_value());
assert_eq!(MinCreateBond::<T>::get(), BalanceOf::<T>::max_value());
assert_eq!(MaxPools::<T>::get(), Some(u32::MAX));
assert_eq!(MaxPoolMembers::<T>::get(), Some(u32::MAX));
assert_eq!(MaxPoolMembersPerPool::<T>::get(), Some(u32::MAX));
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
crate::mock::Runtime
);
}
@@ -0,0 +1,200 @@
// This file is part of Substrate.
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use frame_election_provider_support::VoteWeight;
use frame_support::{pallet_prelude::*, parameter_types, traits::ConstU64, PalletId};
use sp_runtime::traits::{Convert, IdentityLookup};
type AccountId = u128;
type AccountIndex = u32;
type BlockNumber = u64;
type Balance = u128;
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type Origin = Origin;
type Index = AccountIndex;
type BlockNumber = BlockNumber;
type Call = Call;
type Hash = sp_core::H256;
type Hashing = sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = sp_runtime::testing::Header;
type Event = Event;
type BlockHashCount = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_timestamp::Config for Runtime {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = ConstU64<5>;
type WeightInfo = ();
}
parameter_types! {
pub const ExistentialDeposit: Balance = 10;
}
impl pallet_balances::Config for Runtime {
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
}
pallet_staking_reward_curve::build! {
const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!(
min_inflation: 0_025_000,
max_inflation: 0_100_000,
ideal_stake: 0_500_000,
falloff: 0_050_000,
max_piece_count: 40,
test_precision: 0_005_000,
);
}
parameter_types! {
pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS;
}
impl pallet_staking::Config for Runtime {
type MaxNominations = ConstU32<16>;
type Currency = Balances;
type CurrencyBalance = Balance;
type UnixTime = pallet_timestamp::Pallet<Self>;
type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote;
type RewardRemainder = ();
type Event = Event;
type Slash = ();
type Reward = ();
type SessionsPerEra = ();
type SlashDeferDuration = ();
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = ConstU32<3>;
type SessionInterface = ();
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = ();
type MaxNominatorRewardedPerValidator = ConstU32<64>;
type OffendingValidatorsThreshold = ();
type ElectionProvider =
frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking)>;
type GenesisElectionProvider = Self::ElectionProvider;
type VoterList = pallet_bags_list::Pallet<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type OnStakerSlash = Pools;
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
}
parameter_types! {
pub static BagThresholds: &'static [VoteWeight] = &[10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000];
}
impl pallet_bags_list::Config for Runtime {
type Event = Event;
type WeightInfo = ();
type BagThresholds = BagThresholds;
type ScoreProvider = Staking;
type Score = VoteWeight;
}
pub struct BalanceToU256;
impl Convert<Balance, sp_core::U256> for BalanceToU256 {
fn convert(n: Balance) -> sp_core::U256 {
n.into()
}
}
pub struct U256ToBalance;
impl Convert<sp_core::U256, Balance> for U256ToBalance {
fn convert(n: sp_core::U256) -> Balance {
n.try_into().unwrap()
}
}
parameter_types! {
pub static PostUnbondingPoolsWindow: u32 = 10;
pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
}
impl pallet_nomination_pools::Config for Runtime {
type Event = Event;
type WeightInfo = ();
type Currency = Balances;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = Staking;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = ConstU32<8>;
type PalletId = PoolsPalletId;
}
impl crate::Config for Runtime {}
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
where
Call: From<LocalCall>,
{
type OverarchingCall = Call;
type Extrinsic = UncheckedExtrinsic;
}
type Block = frame_system::mocking::MockBlock<Runtime>;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
frame_support::construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Pallet, Call, Event<T>},
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Staking: pallet_staking::{Pallet, Call, Config<T>, Storage, Event<T>},
BagsList: pallet_bags_list::{Pallet, Call, Storage, Event<T>},
Pools: pallet_nomination_pools::{Pallet, Call, Storage, Event<T>},
}
);
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut storage = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
let _ = pallet_nomination_pools::GenesisConfig::<Runtime> {
min_join_bond: 2,
min_create_bond: 2,
max_pools: Some(3),
max_members_per_pool: Some(3),
max_members: Some(3 * 3),
}
.assimilate_storage(&mut storage);
sp_io::TestExternalities::from(storage)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,309 @@
use super::*;
use crate::{self as pools};
use frame_support::{assert_ok, parameter_types, PalletId};
use frame_system::RawOrigin;
use std::collections::HashMap;
pub type AccountId = u128;
pub type Balance = u128;
// Ext builder creates a pool with id 1.
pub fn default_bonded_account() -> AccountId {
Pools::create_bonded_account(1)
}
// Ext builder creates a pool with id 1.
pub fn default_reward_account() -> AccountId {
Pools::create_reward_account(1)
}
parameter_types! {
pub static CurrentEra: EraIndex = 0;
pub static BondingDuration: EraIndex = 3;
static BondedBalanceMap: HashMap<AccountId, Balance> = Default::default();
static UnbondingBalanceMap: HashMap<AccountId, Balance> = Default::default();
#[derive(Clone, PartialEq)]
pub static MaxUnbonding: u32 = 8;
pub static Nominations: Vec<AccountId> = vec![];
}
pub struct StakingMock;
impl StakingMock {
pub(crate) fn set_bonded_balance(who: AccountId, bonded: Balance) {
BONDED_BALANCE_MAP.with(|m| m.borrow_mut().insert(who, bonded));
}
}
impl sp_staking::StakingInterface for StakingMock {
type Balance = Balance;
type AccountId = AccountId;
fn minimum_bond() -> Self::Balance {
10
}
fn current_era() -> EraIndex {
CurrentEra::get()
}
fn bonding_duration() -> EraIndex {
BondingDuration::get()
}
fn active_stake(who: &Self::AccountId) -> Option<Self::Balance> {
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 {
BONDED_BALANCE_MAP.with(|m| *m.borrow_mut().get_mut(&who).unwrap() += extra);
Ok(())
}
fn unbond(who: Self::AccountId, amount: Self::Balance) -> DispatchResult {
BONDED_BALANCE_MAP.with(|m| *m.borrow_mut().get_mut(&who).unwrap() -= amount);
UNBONDING_BALANCE_MAP
.with(|m| *m.borrow_mut().entry(who).or_insert(Self::Balance::zero()) += amount);
Ok(())
}
fn withdraw_unbonded(who: Self::AccountId, _: u32) -> Result<u64, DispatchError> {
// Simulates removing unlocking chunks and only having the bonded balance locked
let _maybe_new_free = UNBONDING_BALANCE_MAP.with(|m| m.borrow_mut().remove(&who));
Ok(100)
}
fn bond(
stash: Self::AccountId,
_: Self::AccountId,
value: Self::Balance,
_: Self::AccountId,
) -> DispatchResult {
StakingMock::set_bonded_balance(stash, value);
Ok(())
}
fn nominate(_: Self::AccountId, nominations: Vec<Self::AccountId>) -> DispatchResult {
Nominations::set(nominations);
Ok(())
}
}
impl frame_system::Config for Runtime {
type SS58Prefix = ();
type BaseCallFilter = frame_support::traits::Everything;
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Call = Call;
type Hash = sp_core::H256;
type Hashing = sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Header = sp_runtime::testing::Header;
type Event = Event;
type BlockHashCount = ();
type DbWeight = ();
type BlockLength = ();
type BlockWeights = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub static ExistentialDeposit: Balance = 5;
}
impl pallet_balances::Config for Runtime {
type MaxLocks = frame_support::traits::ConstU32<1024>;
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
}
pub struct BalanceToU256;
impl Convert<Balance, U256> for BalanceToU256 {
fn convert(n: Balance) -> U256 {
n.into()
}
}
pub struct U256ToBalance;
impl Convert<U256, Balance> for U256ToBalance {
fn convert(n: U256) -> Balance {
n.try_into().unwrap()
}
}
parameter_types! {
pub static PostUnbondingPoolsWindow: u32 = 2;
pub static MaxMetadataLen: u32 = 2;
pub static CheckLevel: u8 = 255;
pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
}
impl pools::Config for Runtime {
type Event = Event;
type WeightInfo = ();
type Currency = Balances;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = StakingMock;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type PalletId = PoolsPalletId;
type MaxMetadataLen = MaxMetadataLen;
type MaxUnbonding = MaxUnbonding;
}
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
type Block = frame_system::mocking::MockBlock<Runtime>;
frame_support::construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Storage, Event<T>, Config},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Pools: pools::{Pallet, Call, Storage, Event<T>},
}
);
#[derive(Default)]
pub struct ExtBuilder {
members: Vec<(AccountId, Balance)>,
}
impl ExtBuilder {
// Add members to pool 0.
pub(crate) fn add_members(mut self, members: Vec<(AccountId, Balance)>) -> Self {
self.members = members;
self
}
pub(crate) fn ed(self, ed: Balance) -> Self {
ExistentialDeposit::set(ed);
self
}
pub(crate) fn with_check(self, level: u8) -> Self {
CheckLevel::set(level);
self
}
pub(crate) fn build(self) -> sp_io::TestExternalities {
let mut storage =
frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
let _ = crate::GenesisConfig::<Runtime> {
min_join_bond: 2,
min_create_bond: 2,
max_pools: Some(2),
max_members_per_pool: Some(3),
max_members: Some(4),
}
.assimilate_storage(&mut storage);
let mut ext = sp_io::TestExternalities::from(storage);
ext.execute_with(|| {
// for events to be deposited.
frame_system::Pallet::<Runtime>::set_block_number(1);
// make a pool
let amount_to_bond = <Runtime as pools::Config>::StakingInterface::minimum_bond();
Balances::make_free_balance_be(&10, amount_to_bond * 2);
assert_ok!(Pools::create(RawOrigin::Signed(10).into(), amount_to_bond, 900, 901, 902));
let last_pool = LastPoolId::<Runtime>::get();
for (account_id, bonded) in self.members {
Balances::make_free_balance_be(&account_id, bonded * 2);
assert_ok!(Pools::join(RawOrigin::Signed(account_id).into(), bonded, last_pool));
}
});
ext
}
pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
self.build().execute_with(|| {
test();
Pools::sanity_checks(CheckLevel::get()).unwrap();
})
}
}
pub(crate) fn unsafe_set_state(pool_id: PoolId, state: PoolState) -> Result<(), ()> {
BondedPools::<Runtime>::try_mutate(pool_id, |maybe_bonded_pool| {
maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
bonded_pool.state = state;
})
})
}
parameter_types! {
static ObservedEvents: usize = 0;
}
/// All events of this pallet.
pub(crate) fn pool_events_since_last_call() -> Vec<super::Event<Runtime>> {
let events = System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| if let Event::Pools(inner) = e { Some(inner) } else { None })
.collect::<Vec<_>>();
let already_seen = ObservedEvents::get();
ObservedEvents::set(events.len());
events.into_iter().skip(already_seen).collect()
}
/// Same as `fully_unbond`, in permissioned setting.
pub fn fully_unbond_permissioned(member: AccountId) -> DispatchResult {
let points = PoolMembers::<Runtime>::get(&member)
.map(|d| d.active_points())
.unwrap_or_default();
Pools::unbond(Origin::signed(member), member, points)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn u256_to_balance_convert_works() {
assert_eq!(U256ToBalance::convert(0u32.into()), Zero::zero());
assert_eq!(U256ToBalance::convert(Balance::max_value().into()), Balance::max_value())
}
#[test]
#[should_panic]
fn u256_to_balance_convert_panics_correctly() {
U256ToBalance::convert(U256::from(Balance::max_value()).saturating_add(1u32.into()));
}
#[test]
fn balance_to_u256_convert_works() {
assert_eq!(BalanceToU256::convert(0u32.into()), U256::zero());
assert_eq!(BalanceToU256::convert(Balance::max_value()), Balance::max_value().into())
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,482 @@
// This file is part of Substrate.
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Autogenerated weights for pallet_nomination_pools
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-04-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/production/substrate
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_nomination_pools
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./frame/nomination-pools/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use sp_std::marker::PhantomData;
/// Weight functions needed for pallet_nomination_pools.
pub trait WeightInfo {
fn join() -> Weight;
fn bond_extra_transfer() -> Weight;
fn bond_extra_reward() -> Weight;
fn claim_payout() -> Weight;
fn unbond() -> Weight;
fn pool_withdraw_unbonded(s: u32, ) -> Weight;
fn withdraw_unbonded_update(s: u32, ) -> Weight;
fn withdraw_unbonded_kill(s: u32, ) -> Weight;
fn create() -> Weight;
fn nominate(n: u32, ) -> Weight;
fn set_state() -> Weight;
fn set_metadata(n: u32, ) -> Weight;
fn set_configs() -> Weight;
}
/// Weights for pallet_nomination_pools using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools MinJoinBond (r:1 w:0)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: Staking Ledger (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:0)
// Storage: System Account (r:2 w:1)
// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0)
// Storage: NominationPools MaxPoolMembers (r:1 w:0)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
// Storage: Staking Bonded (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:3 w:3)
// Storage: BagsList ListBags (r:2 w:2)
fn join() -> Weight {
(117_870_000 as Weight)
.saturating_add(T::DbWeight::get().reads(18 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:2 w:2)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:3 w:3)
// Storage: BagsList ListBags (r:2 w:2)
fn bond_extra_transfer() -> Weight {
(110_176_000 as Weight)
.saturating_add(T::DbWeight::get().reads(14 as Weight))
.saturating_add(T::DbWeight::get().writes(13 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:3 w:3)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:2 w:2)
// Storage: BagsList ListBags (r:2 w:2)
fn bond_extra_reward() -> Weight {
(122_829_000 as Weight)
.saturating_add(T::DbWeight::get().reads(14 as Weight))
.saturating_add(T::DbWeight::get().writes(13 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:1 w:1)
fn claim_payout() -> Weight {
(50_094_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(5 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:2 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Nominators (r:1 w:0)
// Storage: Staking MinNominatorBond (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:3 w:3)
// Storage: Staking Bonded (r:1 w:0)
// Storage: BagsList ListBags (r:2 w:2)
// Storage: NominationPools SubPoolsStorage (r:1 w:1)
// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
fn unbond() -> Weight {
(119_288_000 as Weight)
.saturating_add(T::DbWeight::get().reads(19 as Weight))
.saturating_add(T::DbWeight::get().writes(14 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:0)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
fn pool_withdraw_unbonded(s: u32, ) -> Weight {
(39_986_000 as Weight)
// Standard Error: 0
.saturating_add((50_000 as Weight).saturating_mul(s as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools SubPoolsStorage (r:1 w:1)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Balances Locks (r:1 w:1)
// Storage: System Account (r:1 w:1)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
fn withdraw_unbonded_update(s: u32, ) -> Weight {
(76_897_000 as Weight)
// Standard Error: 0
.saturating_add((48_000 as Weight).saturating_mul(s as Weight))
.saturating_add(T::DbWeight::get().reads(9 as Weight))
.saturating_add(T::DbWeight::get().writes(8 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools SubPoolsStorage (r:1 w:1)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:1)
// Storage: Staking SlashingSpans (r:1 w:0)
// Storage: Staking Validators (r:1 w:0)
// Storage: Staking Nominators (r:1 w:0)
// Storage: System Account (r:2 w:2)
// Storage: Balances Locks (r:1 w:1)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
// Storage: NominationPools ReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: NominationPools CounterForRewardPools (r:1 w:1)
// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
// Storage: NominationPools CounterForBondedPools (r:1 w:1)
// Storage: Staking Payee (r:0 w:1)
fn withdraw_unbonded_kill(_s: u32, ) -> Weight {
(135_837_000 as Weight)
.saturating_add(T::DbWeight::get().reads(20 as Weight))
.saturating_add(T::DbWeight::get().writes(17 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: Staking MinNominatorBond (r:1 w:0)
// Storage: NominationPools MinCreateBond (r:1 w:0)
// Storage: NominationPools MinJoinBond (r:1 w:0)
// Storage: NominationPools MaxPools (r:1 w:0)
// Storage: NominationPools CounterForBondedPools (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools LastPoolId (r:1 w:1)
// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0)
// Storage: NominationPools MaxPoolMembers (r:1 w:0)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
// Storage: System Account (r:2 w:2)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Staking HistoryDepth (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: NominationPools CounterForRewardPools (r:1 w:1)
// Storage: NominationPools ReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: Staking Payee (r:0 w:1)
fn create() -> Weight {
(129_265_000 as Weight)
.saturating_add(T::DbWeight::get().reads(23 as Weight))
.saturating_add(T::DbWeight::get().writes(16 as Weight))
}
// Storage: NominationPools BondedPools (r:1 w:0)
// Storage: Staking Ledger (r:1 w:0)
// Storage: Staking MinNominatorBond (r:1 w:0)
// Storage: Staking Nominators (r:1 w:1)
// Storage: Staking MaxNominatorsCount (r:1 w:0)
// Storage: Staking Validators (r:2 w:0)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Staking Bonded (r:1 w:0)
// Storage: BagsList ListNodes (r:1 w:1)
// Storage: BagsList ListBags (r:1 w:1)
// Storage: BagsList CounterForListNodes (r:1 w:1)
// Storage: Staking CounterForNominators (r:1 w:1)
fn nominate(n: u32, ) -> Weight {
(45_546_000 as Weight)
// Standard Error: 11_000
.saturating_add((2_075_000 as Weight).saturating_mul(n as Weight))
.saturating_add(T::DbWeight::get().reads(12 as Weight))
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight)))
.saturating_add(T::DbWeight::get().writes(5 as Weight))
}
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: Staking Ledger (r:1 w:0)
fn set_state() -> Weight {
(23_256_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: NominationPools BondedPools (r:1 w:0)
// Storage: NominationPools Metadata (r:1 w:1)
// Storage: NominationPools CounterForMetadata (r:1 w:1)
fn set_metadata(n: u32, ) -> Weight {
(10_893_000 as Weight)
// Standard Error: 0
.saturating_add((1_000 as Weight).saturating_mul(n as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
// Storage: NominationPools MinJoinBond (r:0 w:1)
// Storage: NominationPools MaxPoolMembers (r:0 w:1)
// Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1)
// Storage: NominationPools MinCreateBond (r:0 w:1)
// Storage: NominationPools MaxPools (r:0 w:1)
fn set_configs() -> Weight {
(2_793_000 as Weight)
.saturating_add(T::DbWeight::get().writes(5 as Weight))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools MinJoinBond (r:1 w:0)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: Staking Ledger (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:0)
// Storage: System Account (r:2 w:1)
// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0)
// Storage: NominationPools MaxPoolMembers (r:1 w:0)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
// Storage: Staking Bonded (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:3 w:3)
// Storage: BagsList ListBags (r:2 w:2)
fn join() -> Weight {
(117_870_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(18 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:2 w:2)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:3 w:3)
// Storage: BagsList ListBags (r:2 w:2)
fn bond_extra_transfer() -> Weight {
(110_176_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(14 as Weight))
.saturating_add(RocksDbWeight::get().writes(13 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:3 w:3)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:2 w:2)
// Storage: BagsList ListBags (r:2 w:2)
fn bond_extra_reward() -> Weight {
(122_829_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(14 as Weight))
.saturating_add(RocksDbWeight::get().writes(13 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:1 w:1)
fn claim_payout() -> Weight {
(50_094_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(5 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: System Account (r:2 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Nominators (r:1 w:0)
// Storage: Staking MinNominatorBond (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: BagsList ListNodes (r:3 w:3)
// Storage: Staking Bonded (r:1 w:0)
// Storage: BagsList ListBags (r:2 w:2)
// Storage: NominationPools SubPoolsStorage (r:1 w:1)
// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
fn unbond() -> Weight {
(119_288_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(19 as Weight))
.saturating_add(RocksDbWeight::get().writes(14 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:0)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
fn pool_withdraw_unbonded(s: u32, ) -> Weight {
(39_986_000 as Weight)
// Standard Error: 0
.saturating_add((50_000 as Weight).saturating_mul(s as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools SubPoolsStorage (r:1 w:1)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Balances Locks (r:1 w:1)
// Storage: System Account (r:1 w:1)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
fn withdraw_unbonded_update(s: u32, ) -> Weight {
(76_897_000 as Weight)
// Standard Error: 0
.saturating_add((48_000 as Weight).saturating_mul(s as Weight))
.saturating_add(RocksDbWeight::get().reads(9 as Weight))
.saturating_add(RocksDbWeight::get().writes(8 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: NominationPools SubPoolsStorage (r:1 w:1)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:1)
// Storage: Staking SlashingSpans (r:1 w:0)
// Storage: Staking Validators (r:1 w:0)
// Storage: Staking Nominators (r:1 w:0)
// Storage: System Account (r:2 w:2)
// Storage: Balances Locks (r:1 w:1)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
// Storage: NominationPools ReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: NominationPools CounterForRewardPools (r:1 w:1)
// Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1)
// Storage: NominationPools CounterForBondedPools (r:1 w:1)
// Storage: Staking Payee (r:0 w:1)
fn withdraw_unbonded_kill(_s: u32, ) -> Weight {
(135_837_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(20 as Weight))
.saturating_add(RocksDbWeight::get().writes(17 as Weight))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: Staking MinNominatorBond (r:1 w:0)
// Storage: NominationPools MinCreateBond (r:1 w:0)
// Storage: NominationPools MinJoinBond (r:1 w:0)
// Storage: NominationPools MaxPools (r:1 w:0)
// Storage: NominationPools CounterForBondedPools (r:1 w:1)
// Storage: NominationPools PoolMembers (r:1 w:1)
// Storage: NominationPools LastPoolId (r:1 w:1)
// Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0)
// Storage: NominationPools MaxPoolMembers (r:1 w:0)
// Storage: NominationPools CounterForPoolMembers (r:1 w:1)
// Storage: System Account (r:2 w:2)
// Storage: Staking Ledger (r:1 w:1)
// Storage: Staking Bonded (r:1 w:1)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Staking HistoryDepth (r:1 w:0)
// Storage: Balances Locks (r:1 w:1)
// Storage: NominationPools RewardPools (r:1 w:1)
// Storage: NominationPools CounterForRewardPools (r:1 w:1)
// Storage: NominationPools ReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools CounterForReversePoolIdLookup (r:1 w:1)
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: Staking Payee (r:0 w:1)
fn create() -> Weight {
(129_265_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(23 as Weight))
.saturating_add(RocksDbWeight::get().writes(16 as Weight))
}
// Storage: NominationPools BondedPools (r:1 w:0)
// Storage: Staking Ledger (r:1 w:0)
// Storage: Staking MinNominatorBond (r:1 w:0)
// Storage: Staking Nominators (r:1 w:1)
// Storage: Staking MaxNominatorsCount (r:1 w:0)
// Storage: Staking Validators (r:2 w:0)
// Storage: Staking CurrentEra (r:1 w:0)
// Storage: Staking Bonded (r:1 w:0)
// Storage: BagsList ListNodes (r:1 w:1)
// Storage: BagsList ListBags (r:1 w:1)
// Storage: BagsList CounterForListNodes (r:1 w:1)
// Storage: Staking CounterForNominators (r:1 w:1)
fn nominate(n: u32, ) -> Weight {
(45_546_000 as Weight)
// Standard Error: 11_000
.saturating_add((2_075_000 as Weight).saturating_mul(n as Weight))
.saturating_add(RocksDbWeight::get().reads(12 as Weight))
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight)))
.saturating_add(RocksDbWeight::get().writes(5 as Weight))
}
// Storage: NominationPools BondedPools (r:1 w:1)
// Storage: Staking Ledger (r:1 w:0)
fn set_state() -> Weight {
(23_256_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: NominationPools BondedPools (r:1 w:0)
// Storage: NominationPools Metadata (r:1 w:1)
// Storage: NominationPools CounterForMetadata (r:1 w:1)
fn set_metadata(n: u32, ) -> Weight {
(10_893_000 as Weight)
// Standard Error: 0
.saturating_add((1_000 as Weight).saturating_mul(n as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
// Storage: NominationPools MinJoinBond (r:0 w:1)
// Storage: NominationPools MaxPoolMembers (r:0 w:1)
// Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1)
// Storage: NominationPools MinCreateBond (r:0 w:1)
// Storage: NominationPools MaxPools (r:0 w:1)
fn set_configs() -> Weight {
(2_793_000 as Weight)
.saturating_add(RocksDbWeight::get().writes(5 as Weight))
}
}
+1
View File
@@ -71,5 +71,6 @@ runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-election-provider-support/runtime-benchmarks",
"rand_chacha",
"sp-staking/runtime-benchmarks"
]
try-runtime = ["frame-support/try-runtime"]
+1 -1
View File
@@ -49,7 +49,7 @@ type MaxNominators<T> = <<T as Config>::BenchmarkingConfig as BenchmarkingConfig
// Add slashing spans to a user account. Not relevant for actual use, only to benchmark
// read and write operations.
fn add_slashing_spans<T: Config>(who: &T::AccountId, spans: u32) {
pub fn add_slashing_spans<T: Config>(who: &T::AccountId, spans: u32) {
if spans == 0 {
return
}
+12
View File
@@ -737,6 +737,18 @@ where
}
}
impl<AccountId> SessionInterface<AccountId> for () {
fn disable_validator(_: u32) -> bool {
true
}
fn validators() -> Vec<AccountId> {
Vec::new()
}
fn prune_historical_up_to(_: SessionIndex) {
()
}
}
/// Handler for determining how much of a balance should be paid out on the current era.
pub trait EraPayout<Balance> {
/// Determine the payout for this era.
+1 -1
View File
@@ -323,7 +323,7 @@ pub struct ExtBuilder {
invulnerables: Vec<AccountId>,
has_stakers: bool,
initialize_first_session: bool,
min_nominator_bond: Balance,
pub min_nominator_bond: Balance,
min_validator_bond: Balance,
balance_factor: Balance,
status: BTreeMap<AccountId, StakerStatus<AccountId>>,
+68 -3
View File
@@ -29,15 +29,15 @@ use frame_support::{
},
weights::{Weight, WithPostDispatchInfo},
};
use frame_system::pallet_prelude::BlockNumberFor;
use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
use pallet_session::historical;
use sp_runtime::{
traits::{Bounded, Convert, SaturatedConversion, Saturating, Zero},
traits::{Bounded, Convert, SaturatedConversion, Saturating, StaticLookup, Zero},
Perbill,
};
use sp_staking::{
offence::{DisableStrategy, OffenceDetails, OnOffenceHandler},
EraIndex, SessionIndex,
EraIndex, SessionIndex, StakingInterface,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
@@ -1367,3 +1367,68 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsM
Validators::<T>::remove_all();
}
}
impl<T: Config> StakingInterface for Pallet<T> {
type AccountId = T::AccountId;
type Balance = BalanceOf<T>;
fn minimum_bond() -> Self::Balance {
MinNominatorBond::<T>::get()
}
fn bonding_duration() -> EraIndex {
T::BondingDuration::get()
}
fn current_era() -> EraIndex {
Self::current_era().unwrap_or(Zero::zero())
}
fn active_stake(controller: &Self::AccountId) -> Option<Self::Balance> {
Self::ledger(controller).map(|l| l.active)
}
fn total_stake(controller: &Self::AccountId) -> Option<Self::Balance> {
Self::ledger(controller).map(|l| l.total)
}
fn bond_extra(stash: Self::AccountId, extra: Self::Balance) -> DispatchResult {
Self::bond_extra(RawOrigin::Signed(stash).into(), extra)
}
fn unbond(controller: Self::AccountId, value: Self::Balance) -> DispatchResult {
Self::unbond(RawOrigin::Signed(controller).into(), value)
}
fn withdraw_unbonded(
controller: Self::AccountId,
num_slashing_spans: u32,
) -> Result<u64, DispatchError> {
Self::withdraw_unbonded(RawOrigin::Signed(controller).into(), num_slashing_spans)
.map(|post_info| {
post_info
.actual_weight
.unwrap_or(T::WeightInfo::withdraw_unbonded_kill(num_slashing_spans))
})
.map_err(|err_with_post_info| err_with_post_info.error)
}
fn bond(
stash: Self::AccountId,
controller: Self::AccountId,
value: Self::Balance,
payee: Self::AccountId,
) -> DispatchResult {
Self::bond(
RawOrigin::Signed(stash).into(),
T::Lookup::unlookup(controller),
value,
RewardDestination::Account(payee),
)
}
fn nominate(controller: Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
let targets = targets.into_iter().map(T::Lookup::unlookup).collect::<Vec<_>>();
Self::nominate(RawOrigin::Signed(controller).into(), targets)
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ use frame_support::{
use frame_system::{ensure_root, ensure_signed, offchain::SendTransactionTypes, pallet_prelude::*};
use sp_runtime::{
traits::{CheckedSub, SaturatedConversion, StaticLookup, Zero},
DispatchError, Perbill, Percent,
Perbill, Percent,
};
use sp_staking::{EraIndex, SessionIndex};
use sp_std::{cmp::max, prelude::*};
+21
View File
@@ -87,6 +87,8 @@ pub use self::{
StorageHasher, Twox128, Twox256, Twox64Concat,
},
storage::{
bounded_btree_map::BoundedBTreeMap,
bounded_btree_set::BoundedBTreeSet,
bounded_vec::{BoundedSlice, BoundedVec},
migration,
weak_bounded_vec::WeakBoundedVec,
@@ -138,6 +140,25 @@ macro_rules! bounded_vec {
}
}
/// Build a bounded btree-map from the given literals.
///
/// The type of the outcome must be known.
///
/// Will not handle any errors and just panic if the given literals cannot fit in the corresponding
/// bounded vec type. Thus, this is only suitable for testing and non-consensus code.
#[macro_export]
#[cfg(feature = "std")]
macro_rules! bounded_btree_map {
($ ( $key:expr => $value:expr ),* $(,)?) => {
{
$crate::traits::TryCollect::<$crate::BoundedBTreeMap<_, _, _>>::try_collect(
$crate::sp_std::vec![$(($key, $value)),*].into_iter()
).unwrap()
}
};
}
/// Generate a new type alias for [`storage::types::StorageValue`],
/// [`storage::types::StorageMap`], [`storage::types::StorageDoubleMap`]
/// and [`storage::types::StorageNMap`].
@@ -74,6 +74,14 @@ where
Self(t, Default::default())
}
/// Exactly the same semantics as `BTreeMap::retain`.
///
/// The is a safe `&mut self` borrow because `retain` can only ever decrease the length of the
/// inner map.
pub fn retain<F: FnMut(&K, &mut V) -> bool>(&mut self, f: F) {
self.0.retain(f)
}
/// Create a new `BoundedBTreeMap`.
///
/// Does not allocate.
+12 -1
View File
@@ -129,9 +129,13 @@ pub trait DefensiveOption<T> {
/// if `None`, which should never happen.
fn defensive_map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U;
/// Defensively transform this option to a result.
/// Defensively transform this option to a result, mapping `None` to the return value of an
/// error closure.
fn defensive_ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E>;
/// Defensively transform this option to a result, mapping `None` to a default value.
fn defensive_ok_or<E>(self, err: E) -> Result<T, E>;
/// Exactly the same as `map`, but it prints the appropriate warnings if the value being mapped
/// is `None`.
fn defensive_map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U>;
@@ -284,6 +288,13 @@ impl<T> DefensiveOption<T> for Option<T> {
})
}
fn defensive_ok_or<E>(self, err: E) -> Result<T, E> {
self.ok_or_else(|| {
defensive!();
err
})
}
fn defensive_map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
match self {
Some(inner) => Some(f(inner)),
+1
View File
@@ -26,3 +26,4 @@ std = [
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = []
+79 -1
View File
@@ -19,6 +19,7 @@
//! 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 sp_runtime::{DispatchError, DispatchResult};
use sp_std::collections::btree_map::BTreeMap;
pub mod offence;
@@ -38,7 +39,7 @@ pub trait OnStakerSlash<AccountId, Balance> {
///
/// * `stash` - The stash of the staker whom the slash was applied to.
/// * `slashed_active` - The new bonded balance of the staker after the slash was applied.
/// * `slashed_unlocking` - a map of slashed eras, and the balance of that unlocking chunk after
/// * `slashed_unlocking` - A map of slashed eras, and the balance of that unlocking chunk after
/// the slash is applied. Any era not present in the map is not affected at all.
fn on_slash(
stash: &AccountId,
@@ -52,3 +53,80 @@ impl<AccountId, Balance> OnStakerSlash<AccountId, Balance> for () {
// Nothing to do here
}
}
/// Trait for communication with the staking pallet.
pub trait StakingInterface {
/// Balance type used by the staking system.
type Balance;
/// AccountId type used by the staking system
type AccountId;
/// The minimum amount required to bond in order to be a nominator. This does not necessarily
/// mean the nomination will be counted in an election, but instead just enough to be stored as
/// a nominator. In other words, this is the minimum amount to register the intention to
/// nominate.
fn minimum_bond() -> Self::Balance;
/// 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;
/// The current era index.
///
/// This should be the latest planned era that the staking system knows about.
fn current_era() -> EraIndex;
/// The amount of active stake that `controller` has in the staking system.
fn active_stake(controller: &Self::AccountId) -> Option<Self::Balance>;
/// The total stake that `controller` has in the staking system. This includes the
/// [`Self::active_stake`], and any funds currently in the process of unbonding via
/// [`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(controller: &Self::AccountId) -> Option<Self::Balance>;
/// Bond (lock) `value` of `stash`'s balance. `controller` will be set as the account
/// controlling `stash`. This creates what is referred to as "bonded pair".
fn bond(
stash: Self::AccountId,
controller: Self::AccountId,
value: Self::Balance,
payee: Self::AccountId,
) -> DispatchResult;
/// Have `controller` nominate `validators`.
fn nominate(
controller: Self::AccountId,
validators: sp_std::vec::Vec<Self::AccountId>,
) -> DispatchResult;
/// Bond some extra amount in the _Stash_'s free balance against the active bonded balance of
/// the account. The amount extra actually bonded will never be more than the _Stash_'s free
/// balance.
fn bond_extra(controller: Self::AccountId, extra: Self::Balance) -> DispatchResult;
/// Schedule a portion of the active bonded balance to be unlocked at era
/// [Self::current_era] + [`Self::bonding_duration`].
///
/// Once the unlock era has been reached, [`Self::withdraw_unbonded`] can be called to unlock
/// the funds.
///
/// 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
/// schedules have reached their unlocking era should allow more calls to this function.
fn unbond(controller: Self::AccountId, value: Self::Balance) -> DispatchResult;
/// Unlock any funds schedule to unlock before or at the current era.
fn withdraw_unbonded(
controller: Self::AccountId,
num_slashing_spans: u32,
) -> Result<u64, DispatchError>;
}