From 90d158634a57853ee53a4afd9c2a3659a29e4fcb Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 23 Nov 2023 02:06:07 +0100 Subject: [PATCH 001/202] traits for delegation based staking --- .../primitives/staking/src/delegation.rs | 138 ++++++++++++++++++ substrate/primitives/staking/src/lib.rs | 1 + 2 files changed, 139 insertions(+) create mode 100644 substrate/primitives/staking/src/delegation.rs diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs new file mode 100644 index 000000000000..2b6b34211e56 --- /dev/null +++ b/substrate/primitives/staking/src/delegation.rs @@ -0,0 +1,138 @@ +// This file is part of Substrate. + +// Copyright (C) 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 codec::{FullCodec, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{ + DispatchResult, Saturating, +}; +use sp_std::{ops::Sub}; + +/// Allows an account to accept stake delegations and manage its operations. +pub trait StakeDelegatee { + /// Balance type used by the staking system. + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + + /// Total delegated balance to this account. + fn balance(who: Self::AccountId) -> Self::Balance; + + /// Set intention to accept delegations. + fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> DispatchResult; + + /// Stop accepting new delegations on this account. + /// + /// The account would continue to be a delegatee until all delegations to this account has been + /// withdrawn. + fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; + + /// Remove oneself as Delegatee. + /// + /// This will only succeed if all delegations to this delegatee are withdrawn. + fn kill_delegatee(delegatee: &Self::AccountId) -> DispatchResult; + + /// Update bond whenever there is a new delegate funds that are not staked. + fn update_bond(delegatee: &Self::AccountId) -> DispatchResult; + + /// Applies a pending slash on delegatee by passing a delegator account who should be slashed + /// and the value to be slashed. Optionally also takes a reporter account who will be rewarded + /// from part of the slash imbalance. + fn apply_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + reporter: Option, + ) -> DispatchResult; + + /// Migrate a nominator account into a delegatee by moving its funds to delegator account and + /// delegating these funds back to delegatee. + /// + /// Also takes input a payee which will be the new reward destination for the new delegatee. + /// + /// This is useful for migrating old pool accounts to use delegation by providing a pool + /// delegator account. This pool delegator account funds can then lazily move funds to actual + /// delegators using [`Self::delegator_migrate`]. + /// + /// Note: Potentially unsafe and should be only called by trusted runtime code. + fn delegatee_migrate( + new_delegatee: &Self::AccountId, + proxy_delegator: &Self::AccountId, + payee: &Self::AccountId, + ) -> DispatchResult; + + /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining + /// the same. + /// + /// This is useful for migrating old pool accounts using direct staking to lazily move + /// delegators to the new delegated pool account. + /// + /// Note: Potentially unsafe and should be only called by trusted runtime code. + fn delegator_migrate( + delegator_from: &Self::AccountId, + delegator_to: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult; + +} + +/// Allows an account to delegate their stakes to a delegatee. +pub trait StakeDelegator { + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + + /// Delegate some funds to a Delegatee + fn delegate( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult; + + /// Request removal of delegated stake. + fn request_undelegate( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult; + + /// Request removal of delegated stake. + fn withdraw ( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult; +} \ No newline at end of file diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index c2ac5ae004b1..b4fe09b5aeb1 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -32,6 +32,7 @@ use sp_std::{collections::btree_map::BTreeMap, ops::Sub, vec, vec::Vec}; pub mod offence; pub mod currency_to_vote; +mod delegation; /// Simple index type with which we can count sessions. pub type SessionIndex = u32; From e3baa64eaca698aa3bddb36f0feae9c6bf1dd730 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 23 Nov 2023 02:48:18 +0100 Subject: [PATCH 002/202] pallet init --- Cargo.lock | 13 ++++ Cargo.toml | 1 + substrate/frame/staking-delegate/Cargo.toml | 43 +++++++++++ substrate/frame/staking-delegate/src/lib.rs | 80 +++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 substrate/frame/staking-delegate/Cargo.toml create mode 100644 substrate/frame/staking-delegate/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6d976452e4c3..83d8ea617a6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10765,6 +10765,19 @@ dependencies = [ "substrate-test-utils", ] +[[package]] +name = "pallet-staking-delegate" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-std 8.0.0", +] + [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 0091c2d7c8be..98d41bb99e17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -359,6 +359,7 @@ members = [ "substrate/frame/staking/reward-curve", "substrate/frame/staking/reward-fn", "substrate/frame/staking/runtime-api", + "substrate/frame/staking-delegate", "substrate/frame/state-trie-migration", "substrate/frame/statement", "substrate/frame/sudo", diff --git a/substrate/frame/staking-delegate/Cargo.toml b/substrate/frame/staking-delegate/Cargo.toml new file mode 100644 index 000000000000..f9fff42ff10d --- /dev/null +++ b/substrate/frame/staking-delegate/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "pallet-staking-delegate" +version = "4.0.0-dev" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "FRAME safe-mode pallet" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +frame-support = { path = "../support", default-features = false} +frame-system = { path = "../system", default-features = false} +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +sp-std = { path = "../../primitives/std", default-features = false} + +[dev-dependencies] +sp-core = { path = "../../primitives/core" } +sp-io = { path = "../../primitives/io" } + +[features] +default = [ "std" ] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/substrate/frame/staking-delegate/src/lib.rs b/substrate/frame/staking-delegate/src/lib.rs new file mode 100644 index 000000000000..703e9b3cd708 --- /dev/null +++ b/substrate/frame/staking-delegate/src/lib.rs @@ -0,0 +1,80 @@ +// This file is part of Substrate. + +// Copyright (C) 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. + +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(rustdoc::broken_intra_doc_links)] + +use frame_support::{ + pallet_prelude::*, + traits::fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect}, +}; +use frame_system::pallet_prelude::*; +use sp_std::{convert::TryInto, prelude::*}; +use pallet::*; + +pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type Currency: FunHoldMutate< + Self::AccountId, + Reason = Self::RuntimeHoldReason + >; + /// Overarching hold reason. + type RuntimeHoldReason: From; + } + + #[pallet::error] + pub enum Error { + /// The account cannot perform this operation. + NotAllowed, + /// Delegation conditions are not met. + /// + /// Possible issues are + /// 1) Account does not accept or has blocked delegation. + /// 2) Cannot delegate to self, + /// 3) Cannot delegate to multiple Delegatees, + InvalidDelegation, + /// The account does not have enough funds to perform the operation. + NotEnoughFunds, + } + + /// A reason for placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Funds held for stake delegation to another account. + #[codec(index = 0)] + Delegating, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + Delegated { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Withdrawn { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + } +} From 874c1342d9fea36036af71ee3473c222309977eb Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 12:34:41 +0100 Subject: [PATCH 003/202] impl interfaces with todos --- Cargo.lock | 2 + substrate/frame/staking-delegate/Cargo.toml | 7 ++ substrate/frame/staking-delegate/src/lib.rs | 100 ++++++++++++++++++++ substrate/primitives/staking/src/lib.rs | 2 +- 4 files changed, 110 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 83d8ea617a6f..9d8ae5e9d482 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10775,6 +10775,8 @@ dependencies = [ "scale-info", "sp-core", "sp-io", + "sp-runtime", + "sp-staking", "sp-std 8.0.0", ] diff --git a/substrate/frame/staking-delegate/Cargo.toml b/substrate/frame/staking-delegate/Cargo.toml index f9fff42ff10d..ae957c9f5f66 100644 --- a/substrate/frame/staking-delegate/Cargo.toml +++ b/substrate/frame/staking-delegate/Cargo.toml @@ -17,6 +17,8 @@ frame-support = { path = "../support", default-features = false} frame-system = { path = "../system", default-features = false} scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-std = { path = "../../primitives/std", default-features = false} +sp-runtime = { path = "../../primitives/runtime", default-features = false} +sp-staking = { path = "../../primitives/staking", default-features = false } [dev-dependencies] sp-core = { path = "../../primitives/core" } @@ -32,12 +34,17 @@ std = [ "sp-core/std", "sp-io/std", "sp-std/std", + "sp-runtime/std", + "sp-staking/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "sp-staking/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/substrate/frame/staking-delegate/src/lib.rs b/substrate/frame/staking-delegate/src/lib.rs index 703e9b3cd708..fac3847c5f02 100644 --- a/substrate/frame/staking-delegate/src/lib.rs +++ b/substrate/frame/staking-delegate/src/lib.rs @@ -25,6 +25,8 @@ use frame_support::{ use frame_system::pallet_prelude::*; use sp_std::{convert::TryInto, prelude::*}; use pallet::*; +use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug, Saturating}; +use sp_staking::delegation::{StakeDelegatee, StakeDelegator}; pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; @@ -77,4 +79,102 @@ pub mod pallet { Delegated { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, Withdrawn { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, } + + /// Map of Delegators to their delegation. + /// + /// Note: We are not using a double map with delegator and delegatee account as keys since we + /// want to restrict delegators to delegate only to one account. + #[pallet::storage] + pub(crate) type Delegators = + CountedStorageMap<_, Twox64Concat, T::AccountId, (T::AccountId, BalanceOf), OptionQuery>; + + /// Map of Delegatee to their Ledger. + #[pallet::storage] + pub(crate) type Delegatees = CountedStorageMap< + _, + Twox64Concat, + T::AccountId, + DelegationRegister, + OptionQuery, + >; +} + +impl StakeDelegatee for Pallet { + type AccountId = T::AccountId; + type Balance = BalanceOf; + + fn balance(who: Self::AccountId) -> Self::Balance { + todo!() + } + + fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> sp_runtime::DispatchResult { + todo!() + } + + fn block_delegations(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { + todo!() + } + + fn kill_delegatee(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { + todo!() + } + + fn update_bond(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { + todo!() + } + + fn apply_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, reporter: Option) -> sp_runtime::DispatchResult { + todo!() + } + + fn delegatee_migrate(new_delegatee: &Self::AccountId, proxy_delegator: &Self::AccountId, payee: &Self::AccountId) -> sp_runtime::DispatchResult { + todo!() + } + + fn delegator_migrate(delegator_from: &Self::AccountId, delegator_to: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + todo!() + } +} + +impl StakeDelegator for Pallet { + type AccountId = T::AccountId; + type Balance = BalanceOf; + + fn delegate(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + todo!() + } + + fn request_undelegate(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + todo!() + } + + fn withdraw(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + todo!() + } +} + +/// Register of all delegations to a `Delegatee`. +/// +/// This keeps track of the active balance of the delegatee that is made up from the funds that are +/// currently delegated to this delegatee. It also tracks the pending slashes yet to be applied +/// among other things. +#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct DelegationRegister { + /// Where the reward should be paid out. + pub payee: T::AccountId, + /// Sum of all delegated funds to this delegatee. + #[codec(compact)] + pub balance: BalanceOf, + /// Slashes that are not yet applied. + #[codec(compact)] + pub pending_slash: BalanceOf, + /// Whether this delegatee is blocked from receiving new delegations. + pub blocked: bool, +} + +impl DelegationRegister { + pub fn effective_balance(&self) -> BalanceOf { + self.balance.saturating_sub(self.pending_slash) + } } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index b4fe09b5aeb1..74fbbc7419f4 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -32,7 +32,7 @@ use sp_std::{collections::btree_map::BTreeMap, ops::Sub, vec, vec::Vec}; pub mod offence; pub mod currency_to_vote; -mod delegation; +pub mod delegation; /// Simple index type with which we can count sessions. pub type SessionIndex = u32; From af14ddf37ef9b5b8359dd7703165cdac6060a465 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 12:51:56 +0100 Subject: [PATCH 004/202] compiles with copy impl from old branch --- Cargo.lock | 30 ++--- Cargo.toml | 2 +- .../Cargo.toml | 2 +- .../src/lib.rs | 110 ++++++++++++++++-- .../primitives/staking/src/delegation.rs | 2 +- 5 files changed, 120 insertions(+), 26 deletions(-) rename substrate/frame/{staking-delegate => delegated-staking}/Cargo.toml (97%) rename substrate/frame/{staking-delegate => delegated-staking}/src/lib.rs (62%) diff --git a/Cargo.lock b/Cargo.lock index 9d8ae5e9d482..fb275c898890 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9676,6 +9676,21 @@ dependencies = [ "sp-std 8.0.0", ] +[[package]] +name = "pallet-delegated-staking" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std 8.0.0", +] + [[package]] name = "pallet-democracy" version = "4.0.0-dev" @@ -10765,21 +10780,6 @@ dependencies = [ "substrate-test-utils", ] -[[package]] -name = "pallet-staking-delegate" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-staking", - "sp-std 8.0.0", -] - [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 98d41bb99e17..285a6349fffa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -359,7 +359,7 @@ members = [ "substrate/frame/staking/reward-curve", "substrate/frame/staking/reward-fn", "substrate/frame/staking/runtime-api", - "substrate/frame/staking-delegate", + "substrate/frame/delegated-staking", "substrate/frame/state-trie-migration", "substrate/frame/statement", "substrate/frame/sudo", diff --git a/substrate/frame/staking-delegate/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml similarity index 97% rename from substrate/frame/staking-delegate/Cargo.toml rename to substrate/frame/delegated-staking/Cargo.toml index ae957c9f5f66..cb2d571844ea 100644 --- a/substrate/frame/staking-delegate/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pallet-staking-delegate" +name = "pallet-delegated-staking" version = "4.0.0-dev" authors.workspace = true edition.workspace = true diff --git a/substrate/frame/staking-delegate/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs similarity index 62% rename from substrate/frame/staking-delegate/src/lib.rs rename to substrate/frame/delegated-staking/src/lib.rs index fac3847c5f02..177bd57ea632 100644 --- a/substrate/frame/staking-delegate/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -20,8 +20,9 @@ use frame_support::{ pallet_prelude::*, - traits::fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect}, + traits::{fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect}, tokens::Precision}, }; +use frame_support::traits::tokens::{Fortitude, Preservation}; use frame_system::pallet_prelude::*; use sp_std::{convert::TryInto, prelude::*}; use pallet::*; @@ -63,6 +64,8 @@ pub mod pallet { InvalidDelegation, /// The account does not have enough funds to perform the operation. NotEnoughFunds, + /// Not an existing delegatee account. + NotDelegatee, } /// A reason for placing a hold on funds. @@ -100,19 +103,44 @@ pub mod pallet { } impl StakeDelegatee for Pallet { - type AccountId = T::AccountId; type Balance = BalanceOf; + type AccountId = T::AccountId; - fn balance(who: Self::AccountId) -> Self::Balance { - todo!() + fn delegate_balance(who: Self::AccountId) -> Self::Balance { + >::get(who) + .map_or_else(|| 0u32.into(), |register| register.effective_balance()) } fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> sp_runtime::DispatchResult { - todo!() + // fail if already delegatee + ensure!(!>::contains_key(delegatee), Error::::NotAllowed); + // a delegator cannot be delegatee + ensure!(!>::contains_key(delegatee), Error::::NotAllowed); + // payee account cannot be same as delegatee + ensure!(payee != delegatee, Error::::InvalidDelegation); + + >::insert( + delegatee, + DelegationRegister { + payee: payee.clone(), + balance: Zero::zero(), + pending_slash: Zero::zero(), + blocked: false, + }, + ); + + Ok(()) } fn block_delegations(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { - todo!() + >::mutate(delegatee, |maybe_register| { + if let Some(register) = maybe_register { + register.blocked = true; + Ok(()) + } else { + Err(Error::::NotDelegatee.into()) + } + }) } fn kill_delegatee(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { @@ -141,7 +169,35 @@ impl StakeDelegator for Pallet { type Balance = BalanceOf; fn delegate(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { - todo!() + let delegator_balance = T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); + ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); + ensure!(delegator_balance >= value, Error::::NotEnoughFunds); + ensure!(delegatee != delegator, Error::::InvalidDelegation); + ensure!(>::contains_key(delegatee), Error::::NotDelegatee); + + // cannot delegate to another delegatee. + if >::contains_key(delegator) { + return Err(Error::::InvalidDelegation.into()) + } + + let new_delegation_amount = + if let Some((current_delegatee, current_delegation)) = >::get(delegator) { + ensure!(¤t_delegatee == delegatee, Error::::InvalidDelegation); + value.saturating_add(current_delegation) + } else { + value + }; + + >::insert(delegator, (delegatee, new_delegation_amount)); + >::mutate(delegatee, |maybe_register| { + if let Some(register) = maybe_register { + register.balance.saturating_accrue(value); + } + }); + + T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; + + Ok(()) } fn request_undelegate(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { @@ -149,7 +205,45 @@ impl StakeDelegator for Pallet { } fn withdraw(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { - todo!() + >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { + Some((current_delegatee, delegate_balance)) => { + ensure!(¤t_delegatee.clone() == delegatee, Error::::InvalidDelegation); + ensure!(*delegate_balance >= value, Error::::InvalidDelegation); + + delegate_balance.saturating_reduce(value); + + if *delegate_balance == BalanceOf::::zero() { + *maybe_delegate = None; + } + Ok(()) + }, + None => { + // this should never happen + return Err(Error::::InvalidDelegation) + }, + })?; + + >::mutate(delegatee, |maybe_register| match maybe_register { + Some(ledger) => { + ledger.balance.saturating_reduce(value); + Ok(()) + }, + None => { + // this should never happen + return Err(Error::::InvalidDelegation) + }, + })?; + + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &delegator, + value, + Precision::BestEffort, + )?; + + defensive_assert!(released == value, "hold should have been released fully"); + + Ok(()) } } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 2b6b34211e56..1837b2cc2bb1 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -39,7 +39,7 @@ pub trait StakeDelegatee { type AccountId: Clone + sp_std::fmt::Debug; /// Total delegated balance to this account. - fn balance(who: Self::AccountId) -> Self::Balance; + fn delegate_balance(who: Self::AccountId) -> Self::Balance; /// Set intention to accept delegations. fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> DispatchResult; From e1ec9b20bf84611cb207ee3265637ef1f3e0a9a4 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 13:22:55 +0100 Subject: [PATCH 005/202] build it as a wrapper over core staking --- substrate/frame/delegated-staking/src/lib.rs | 162 ++++++++++++++++--- 1 file changed, 139 insertions(+), 23 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 177bd57ea632..f734c74cb433 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -28,6 +28,7 @@ use sp_std::{convert::TryInto, prelude::*}; use pallet::*; use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug, Saturating}; use sp_staking::delegation::{StakeDelegatee, StakeDelegator}; +use sp_staking::{EraIndex, Stake, StakerStatus, StakingInterface}; pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; @@ -49,6 +50,9 @@ pub mod pallet { >; /// Overarching hold reason. type RuntimeHoldReason: From; + + /// Core staking implementation. + type Staking: StakingInterface, AccountId = Self::AccountId>; } #[pallet::error] @@ -102,6 +106,32 @@ pub mod pallet { >; } +/// Register of all delegations to a `Delegatee`. +/// +/// This keeps track of the active balance of the delegatee that is made up from the funds that are +/// currently delegated to this delegatee. It also tracks the pending slashes yet to be applied +/// among other things. +#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct DelegationRegister { + /// Where the reward should be paid out. + pub payee: T::AccountId, + /// Sum of all delegated funds to this delegatee. + #[codec(compact)] + pub balance: BalanceOf, + /// Slashes that are not yet applied. + #[codec(compact)] + pub pending_slash: BalanceOf, + /// Whether this delegatee is blocked from receiving new delegations. + pub blocked: bool, +} + +impl DelegationRegister { + pub fn effective_balance(&self) -> BalanceOf { + self.balance.saturating_sub(self.pending_slash) + } +} + impl StakeDelegatee for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -247,28 +277,114 @@ impl StakeDelegator for Pallet { } } -/// Register of all delegations to a `Delegatee`. -/// -/// This keeps track of the active balance of the delegatee that is made up from the funds that are -/// currently delegated to this delegatee. It also tracks the pending slashes yet to be applied -/// among other things. -#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -pub struct DelegationRegister { - /// Where the reward should be paid out. - pub payee: T::AccountId, - /// Sum of all delegated funds to this delegatee. - #[codec(compact)] - pub balance: BalanceOf, - /// Slashes that are not yet applied. - #[codec(compact)] - pub pending_slash: BalanceOf, - /// Whether this delegatee is blocked from receiving new delegations. - pub blocked: bool, -} +impl StakingInterface for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + type CurrencyToVote = ::CurrencyToVote; -impl DelegationRegister { - pub fn effective_balance(&self) -> BalanceOf { - self.balance.saturating_sub(self.pending_slash) + fn minimum_nominator_bond() -> Self::Balance { + T::Staking::minimum_nominator_bond() } -} + + fn minimum_validator_bond() -> Self::Balance { + T::Staking::minimum_validator_bond() + } + + fn stash_by_ctrl(controller: &Self::AccountId) -> Result { + T::Staking::stash_by_ctrl(controller) + } + + fn bonding_duration() -> EraIndex { + T::Staking::bonding_duration() + } + + fn current_era() -> EraIndex { + T::Staking::current_era() + } + + fn stake(who: &Self::AccountId) -> Result, DispatchError> { + T::Staking::stake(who) + } + + fn total_stake(who: &Self::AccountId) -> Result { + T::Staking::total_stake(who) + } + + fn active_stake(who: &Self::AccountId) -> Result { + T::Staking::active_stake(who) + } + + fn is_unbonding(who: &Self::AccountId) -> Result { + T::Staking::is_unbonding(who) + } + + fn fully_unbond(who: &Self::AccountId) -> sp_runtime::DispatchResult { + T::Staking::fully_unbond(who) + } + + fn bond(who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId) -> sp_runtime::DispatchResult { + T::Staking::bond(who, value, payee) + } + + fn nominate(who: &Self::AccountId, validators: Vec) -> sp_runtime::DispatchResult { + T::Staking::nominate(who, validators) + } + + fn chill(who: &Self::AccountId) -> sp_runtime::DispatchResult { + T::Staking::chill(who) + } + + fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> sp_runtime::DispatchResult { + T::Staking::bond_extra(who, extra) + } + + fn unbond(stash: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + T::Staking::unbond(stash, value) + } + + fn withdraw_unbonded(stash: Self::AccountId, num_slashing_spans: u32) -> Result { + T::Staking::withdraw_unbonded(stash, num_slashing_spans) + } + + fn desired_validator_count() -> u32 { + T::Staking::desired_validator_count() + } + + fn election_ongoing() -> bool { + T::Staking::election_ongoing() + } + + fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult { + T::Staking::force_unstake(who) + } + + fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { + T::Staking::is_exposed_in_era(who, era) + } + + fn status(who: &Self::AccountId) -> Result, DispatchError> { + T::Staking::status(who) + } + + fn is_validator(who: &Self::AccountId) -> bool { + T::Staking::is_validator(who) + } + + fn nominations(who: &Self::AccountId) -> Option> { + T::Staking::nominations(who) + } + + #[cfg(feature = "runtime-benchmarks")] + fn max_exposure_page_size() -> sp_staking::Page { + T::Staking::max_exposure_page_size() + } + + #[cfg(feature = "runtime-benchmarks")] + fn add_era_stakers(current_era: &EraIndex, stash: &Self::AccountId, exposures: Vec<(Self::AccountId, Self::Balance)>) { + T::Staking::add_era_stakers(current_era, stash, exposures) + } + #[cfg(feature = "runtime-benchmarks")] + fn set_current_era(era: EraIndex) { + T::Staking::set_current_era(era) + } +} \ No newline at end of file From 7f9186910d7cc0de4fb27806ff321a24560cf99a Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 16:38:47 +0100 Subject: [PATCH 006/202] refactor --- substrate/frame/delegated-staking/src/lib.rs | 162 +++++++++++------- .../primitives/staking/src/delegation.rs | 32 ++-- 2 files changed, 120 insertions(+), 74 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index f734c74cb433..813443fcb06f 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -20,17 +20,22 @@ use frame_support::{ pallet_prelude::*, - traits::{fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect}, tokens::Precision}, + traits::{ + fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect}, + tokens::{Fortitude, Precision, Preservation}, + }, }; -use frame_support::traits::tokens::{Fortitude, Preservation}; use frame_system::pallet_prelude::*; -use sp_std::{convert::TryInto, prelude::*}; use pallet::*; use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug, Saturating}; -use sp_staking::delegation::{StakeDelegatee, StakeDelegator}; -use sp_staking::{EraIndex, Stake, StakerStatus, StakingInterface}; +use sp_staking::{ + delegation::{Delegatee, Delegator}, + EraIndex, Stake, StakerStatus, StakingInterface, +}; +use sp_std::{convert::TryInto, prelude::*}; -pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; +pub type BalanceOf = + <::Currency as FunInspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { @@ -44,10 +49,7 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type Currency: FunHoldMutate< - Self::AccountId, - Reason = Self::RuntimeHoldReason - >; + type Currency: FunHoldMutate; /// Overarching hold reason. type RuntimeHoldReason: From; @@ -93,17 +95,12 @@ pub mod pallet { /// want to restrict delegators to delegate only to one account. #[pallet::storage] pub(crate) type Delegators = - CountedStorageMap<_, Twox64Concat, T::AccountId, (T::AccountId, BalanceOf), OptionQuery>; + CountedStorageMap<_, Twox64Concat, T::AccountId, (T::AccountId, BalanceOf), OptionQuery>; /// Map of Delegatee to their Ledger. #[pallet::storage] - pub(crate) type Delegatees = CountedStorageMap< - _, - Twox64Concat, - T::AccountId, - DelegationRegister, - OptionQuery, - >; + pub(crate) type Delegatees = + CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationRegister, OptionQuery>; } /// Register of all delegations to a `Delegatee`. @@ -132,7 +129,7 @@ impl DelegationRegister { } } -impl StakeDelegatee for Pallet { +impl Delegatee for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -141,23 +138,30 @@ impl StakeDelegatee for Pallet { .map_or_else(|| 0u32.into(), |register| register.effective_balance()) } - fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> sp_runtime::DispatchResult { - // fail if already delegatee - ensure!(!>::contains_key(delegatee), Error::::NotAllowed); - // a delegator cannot be delegatee - ensure!(!>::contains_key(delegatee), Error::::NotAllowed); + fn accept_delegations( + who: &Self::AccountId, + payee: &Self::AccountId, + ) -> sp_runtime::DispatchResult { + // Existing delegatee cannot accept delegation + ensure!(!>::contains_key(who), Error::::NotAllowed); + // payee account cannot be same as delegatee - ensure!(payee != delegatee, Error::::InvalidDelegation); - - >::insert( - delegatee, - DelegationRegister { - payee: payee.clone(), - balance: Zero::zero(), - pending_slash: Zero::zero(), - blocked: false, - }, - ); + ensure!(payee != who, Error::::InvalidDelegation); + + // if already a delegator, unblock and return success + >::mutate(who, |maybe_register| { + if let Some(register) = maybe_register { + register.blocked = false; + register.payee = payee.clone(); + } else { + *maybe_register = Some(DelegationRegister { + payee: payee.clone(), + balance: Zero::zero(), + pending_slash: Zero::zero(), + blocked: false, + }); + } + }); Ok(()) } @@ -181,25 +185,35 @@ impl StakeDelegatee for Pallet { todo!() } - fn apply_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, reporter: Option) -> sp_runtime::DispatchResult { + fn apply_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + reporter: Option, + ) -> sp_runtime::DispatchResult { todo!() } - fn delegatee_migrate(new_delegatee: &Self::AccountId, proxy_delegator: &Self::AccountId, payee: &Self::AccountId) -> sp_runtime::DispatchResult { - todo!() - } - - fn delegator_migrate(delegator_from: &Self::AccountId, delegator_to: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + fn delegatee_migrate( + new_delegatee: &Self::AccountId, + proxy_delegator: &Self::AccountId, + payee: &Self::AccountId, + ) -> sp_runtime::DispatchResult { todo!() } } -impl StakeDelegator for Pallet { +impl Delegator for Pallet { type AccountId = T::AccountId; type Balance = BalanceOf; - fn delegate(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { - let delegator_balance = T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); + fn delegate( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> sp_runtime::DispatchResult { + let delegator_balance = + T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); ensure!(delegator_balance >= value, Error::::NotEnoughFunds); ensure!(delegatee != delegator, Error::::InvalidDelegation); @@ -210,13 +224,14 @@ impl StakeDelegator for Pallet { return Err(Error::::InvalidDelegation.into()) } - let new_delegation_amount = - if let Some((current_delegatee, current_delegation)) = >::get(delegator) { - ensure!(¤t_delegatee == delegatee, Error::::InvalidDelegation); - value.saturating_add(current_delegation) - } else { - value - }; + let new_delegation_amount = if let Some((current_delegatee, current_delegation)) = + >::get(delegator) + { + ensure!(¤t_delegatee == delegatee, Error::::InvalidDelegation); + value.saturating_add(current_delegation) + } else { + value + }; >::insert(delegator, (delegatee, new_delegation_amount)); >::mutate(delegatee, |maybe_register| { @@ -230,11 +245,19 @@ impl StakeDelegator for Pallet { Ok(()) } - fn request_undelegate(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + fn request_undelegate( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> sp_runtime::DispatchResult { todo!() } - fn withdraw(delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { + fn withdraw( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> sp_runtime::DispatchResult { >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { Some((current_delegatee, delegate_balance)) => { ensure!(¤t_delegatee.clone() == delegatee, Error::::InvalidDelegation); @@ -275,6 +298,15 @@ impl StakeDelegator for Pallet { Ok(()) } + + fn delegator_migrate( + delegator_from: &Self::AccountId, + delegator_to: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> sp_runtime::DispatchResult { + todo!() + } } impl StakingInterface for Pallet { @@ -322,11 +354,18 @@ impl StakingInterface for Pallet { T::Staking::fully_unbond(who) } - fn bond(who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId) -> sp_runtime::DispatchResult { + fn bond( + who: &Self::AccountId, + value: Self::Balance, + payee: &Self::AccountId, + ) -> sp_runtime::DispatchResult { T::Staking::bond(who, value, payee) } - fn nominate(who: &Self::AccountId, validators: Vec) -> sp_runtime::DispatchResult { + fn nominate( + who: &Self::AccountId, + validators: Vec, + ) -> sp_runtime::DispatchResult { T::Staking::nominate(who, validators) } @@ -342,7 +381,10 @@ impl StakingInterface for Pallet { T::Staking::unbond(stash, value) } - fn withdraw_unbonded(stash: Self::AccountId, num_slashing_spans: u32) -> Result { + fn withdraw_unbonded( + stash: Self::AccountId, + num_slashing_spans: u32, + ) -> Result { T::Staking::withdraw_unbonded(stash, num_slashing_spans) } @@ -380,11 +422,15 @@ impl StakingInterface for Pallet { } #[cfg(feature = "runtime-benchmarks")] - fn add_era_stakers(current_era: &EraIndex, stash: &Self::AccountId, exposures: Vec<(Self::AccountId, Self::Balance)>) { + fn add_era_stakers( + current_era: &EraIndex, + stash: &Self::AccountId, + exposures: Vec<(Self::AccountId, Self::Balance)>, + ) { T::Staking::add_era_stakers(current_era, stash, exposures) } #[cfg(feature = "runtime-benchmarks")] fn set_current_era(era: EraIndex) { T::Staking::set_current_era(era) } -} \ No newline at end of file +} diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 1837b2cc2bb1..7154d2c8c667 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -23,7 +23,7 @@ use sp_runtime::{ use sp_std::{ops::Sub}; /// Allows an account to accept stake delegations and manage its operations. -pub trait StakeDelegatee { +pub trait Delegatee { /// Balance type used by the staking system. type Balance: Sub + Ord @@ -84,24 +84,10 @@ pub trait StakeDelegatee { payee: &Self::AccountId, ) -> DispatchResult; - /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining - /// the same. - /// - /// This is useful for migrating old pool accounts using direct staking to lazily move - /// delegators to the new delegated pool account. - /// - /// Note: Potentially unsafe and should be only called by trusted runtime code. - fn delegator_migrate( - delegator_from: &Self::AccountId, - delegator_to: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult; - } /// Allows an account to delegate their stakes to a delegatee. -pub trait StakeDelegator { +pub trait Delegator { type Balance: Sub + Ord + PartialEq @@ -135,4 +121,18 @@ pub trait StakeDelegator { delegatee: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; + + /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining + /// the same. + /// + /// This is useful for migrating old pool accounts using direct staking to lazily move + /// delegators to the new delegated pool account. + /// + /// Note: Potentially unsafe and should be only called by trusted runtime code. + fn delegator_migrate( + delegator_from: &Self::AccountId, + delegator_to: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult; } \ No newline at end of file From 7a9f4de28a6d1edfb4eb049d3d8759e39bb81e06 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 17:26:10 +0100 Subject: [PATCH 007/202] finish refactoring delegatee --- substrate/frame/delegated-staking/src/lib.rs | 187 ++++++------------- substrate/frame/staking/src/pallet/impls.rs | 5 + substrate/primitives/staking/src/lib.rs | 3 + 3 files changed, 63 insertions(+), 132 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 813443fcb06f..24d528f237f2 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -21,8 +21,9 @@ use frame_support::{ pallet_prelude::*, traits::{ - fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect}, + fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, tokens::{Fortitude, Precision, Preservation}, + ExistenceRequirement, }, }; use frame_system::pallet_prelude::*; @@ -49,7 +50,8 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type Currency: FunHoldMutate; + type Currency: FunHoldMutate + + FunMutate; /// Overarching hold reason. type RuntimeHoldReason: From; @@ -72,6 +74,8 @@ pub mod pallet { NotEnoughFunds, /// Not an existing delegatee account. NotDelegatee, + /// Some corruption in internal state. + BadState, } /// A reason for placing a hold on funds. @@ -181,8 +185,20 @@ impl Delegatee for Pallet { todo!() } - fn update_bond(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { - todo!() + fn update_bond(who: &Self::AccountId) -> sp_runtime::DispatchResult { + let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; + let delegated_balance = delegatee.effective_balance(); + + match T::Staking::stake(who) { + Ok(stake) => { + let unstaked_delegated_balance = delegated_balance.saturating_sub(stake.total); + T::Staking::bond_extra(who, unstaked_delegated_balance) + }, + Err(_) => { + // If stake not found, it means this is the first bond + T::Staking::bond(who, delegated_balance, &delegatee.payee) + }, + } } fn apply_slash( @@ -194,18 +210,51 @@ impl Delegatee for Pallet { todo!() } + /// Transfers funds from current staked account to `proxy_delegator`. Current staked account + /// becomes a delegatee with `proxy_delegator` delegating stakes to it. fn delegatee_migrate( new_delegatee: &Self::AccountId, proxy_delegator: &Self::AccountId, payee: &Self::AccountId, ) -> sp_runtime::DispatchResult { - todo!() + ensure!(new_delegatee != proxy_delegator, Error::::InvalidDelegation); + + // ensure proxy delegator has at least minimum balance to keep the account alive. + ensure!( + T::Currency::reducible_balance( + proxy_delegator, + Preservation::Expendable, + Fortitude::Polite + ) > Zero::zero(), + Error::::NotEnoughFunds + ); + + // ensure staker is a nominator + let status = T::Staking::status(new_delegatee)?; + match status { + StakerStatus::Nominator(_) => (), + _ => return Err(Error::::InvalidDelegation.into()), + } + + let stake = T::Staking::stake(new_delegatee)?; + + // unlock funds from staker + T::Staking::force_unlock(new_delegatee)?; + + // try transferring the staked amount. This should never fail but if it does, it indicates + // bad state and we abort. + T::Currency::transfer(new_delegatee, proxy_delegator, stake.total, Preservation::Expendable) + .map_err(|_| Error::::BadState)?; + + // delegate from new delegator to staker. + Self::accept_delegations(new_delegatee, payee)?; + Self::delegate(proxy_delegator, new_delegatee, stake.total) } } impl Delegator for Pallet { - type AccountId = T::AccountId; type Balance = BalanceOf; + type AccountId = T::AccountId; fn delegate( delegator: &Self::AccountId, @@ -308,129 +357,3 @@ impl Delegator for Pallet { todo!() } } - -impl StakingInterface for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - type CurrencyToVote = ::CurrencyToVote; - - fn minimum_nominator_bond() -> Self::Balance { - T::Staking::minimum_nominator_bond() - } - - fn minimum_validator_bond() -> Self::Balance { - T::Staking::minimum_validator_bond() - } - - fn stash_by_ctrl(controller: &Self::AccountId) -> Result { - T::Staking::stash_by_ctrl(controller) - } - - fn bonding_duration() -> EraIndex { - T::Staking::bonding_duration() - } - - fn current_era() -> EraIndex { - T::Staking::current_era() - } - - fn stake(who: &Self::AccountId) -> Result, DispatchError> { - T::Staking::stake(who) - } - - fn total_stake(who: &Self::AccountId) -> Result { - T::Staking::total_stake(who) - } - - fn active_stake(who: &Self::AccountId) -> Result { - T::Staking::active_stake(who) - } - - fn is_unbonding(who: &Self::AccountId) -> Result { - T::Staking::is_unbonding(who) - } - - fn fully_unbond(who: &Self::AccountId) -> sp_runtime::DispatchResult { - T::Staking::fully_unbond(who) - } - - fn bond( - who: &Self::AccountId, - value: Self::Balance, - payee: &Self::AccountId, - ) -> sp_runtime::DispatchResult { - T::Staking::bond(who, value, payee) - } - - fn nominate( - who: &Self::AccountId, - validators: Vec, - ) -> sp_runtime::DispatchResult { - T::Staking::nominate(who, validators) - } - - fn chill(who: &Self::AccountId) -> sp_runtime::DispatchResult { - T::Staking::chill(who) - } - - fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> sp_runtime::DispatchResult { - T::Staking::bond_extra(who, extra) - } - - fn unbond(stash: &Self::AccountId, value: Self::Balance) -> sp_runtime::DispatchResult { - T::Staking::unbond(stash, value) - } - - fn withdraw_unbonded( - stash: Self::AccountId, - num_slashing_spans: u32, - ) -> Result { - T::Staking::withdraw_unbonded(stash, num_slashing_spans) - } - - fn desired_validator_count() -> u32 { - T::Staking::desired_validator_count() - } - - fn election_ongoing() -> bool { - T::Staking::election_ongoing() - } - - fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult { - T::Staking::force_unstake(who) - } - - fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { - T::Staking::is_exposed_in_era(who, era) - } - - fn status(who: &Self::AccountId) -> Result, DispatchError> { - T::Staking::status(who) - } - - fn is_validator(who: &Self::AccountId) -> bool { - T::Staking::is_validator(who) - } - - fn nominations(who: &Self::AccountId) -> Option> { - T::Staking::nominations(who) - } - - #[cfg(feature = "runtime-benchmarks")] - fn max_exposure_page_size() -> sp_staking::Page { - T::Staking::max_exposure_page_size() - } - - #[cfg(feature = "runtime-benchmarks")] - fn add_era_stakers( - current_era: &EraIndex, - stash: &Self::AccountId, - exposures: Vec<(Self::AccountId, Self::Balance)>, - ) { - T::Staking::add_era_stakers(current_era, stash, exposures) - } - #[cfg(feature = "runtime-benchmarks")] - fn set_current_era(era: EraIndex) { - T::Staking::set_current_era(era) - } -} diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index d294686751cd..26af96dd54e0 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1815,6 +1815,11 @@ impl StakingInterface for Pallet { T::MaxExposurePageSize::get() } } + + fn force_unlock(who: &Self::AccountId) -> sp_runtime::DispatchResult { + T::Currency::remove_lock(crate::STAKING_ID, who); + Ok(()) + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 74fbbc7419f4..9be0e338af33 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -285,6 +285,9 @@ pub trait StakingInterface { } } + /// Used for migration of locks. + fn force_unlock(who: &Self::AccountId) -> DispatchResult; + #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> Page; From 4f52e4916a3a9c5eb8b0abb8abdd193160dbc678 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 17:42:38 +0100 Subject: [PATCH 008/202] refactor migrate functions --- substrate/frame/delegated-staking/src/lib.rs | 116 ++++++++++-------- .../primitives/staking/src/delegation.rs | 58 ++++----- 2 files changed, 94 insertions(+), 80 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 24d528f237f2..e3a1b48df450 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -201,6 +201,52 @@ impl Delegatee for Pallet { } } + fn withdraw( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + value: Self::Balance, + ) -> sp_runtime::DispatchResult { + >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { + Some((current_delegatee, delegate_balance)) => { + ensure!(¤t_delegatee.clone() == delegatee, Error::::NotDelegatee); + ensure!(*delegate_balance >= value, Error::::NotAllowed); + + delegate_balance.saturating_reduce(value); + + if *delegate_balance == BalanceOf::::zero() { + *maybe_delegate = None; + } + Ok(()) + }, + None => { + // delegator does not exist + return Err(Error::::NotAllowed) + }, + })?; + + >::mutate(delegatee, |maybe_register| match maybe_register { + Some(ledger) => { + ledger.balance.saturating_reduce(value); + Ok(()) + }, + None => { + // Delegatee not found + return Err(Error::::NotDelegatee) + }, + })?; + + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &delegator, + value, + Precision::BestEffort, + )?; + + defensive_assert!(released == value, "hold should have been released fully"); + + Ok(()) + } + fn apply_slash( delegatee: &Self::AccountId, delegator: &Self::AccountId, @@ -243,8 +289,13 @@ impl Delegatee for Pallet { // try transferring the staked amount. This should never fail but if it does, it indicates // bad state and we abort. - T::Currency::transfer(new_delegatee, proxy_delegator, stake.total, Preservation::Expendable) - .map_err(|_| Error::::BadState)?; + T::Currency::transfer( + new_delegatee, + proxy_delegator, + stake.total, + Preservation::Expendable, + ) + .map_err(|_| Error::::BadState)?; // delegate from new delegator to staker. Self::accept_delegations(new_delegatee, payee)?; @@ -302,58 +353,27 @@ impl Delegator for Pallet { todo!() } - fn withdraw( - delegator: &Self::AccountId, + /// Move funds from proxy delegator to actual delegator. + // TODO: Keep track of proxy delegator and only allow movement from proxy -> new delegator + fn delegator_migrate( + existing_delegator: &Self::AccountId, + new_delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance, ) -> sp_runtime::DispatchResult { - >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { - Some((current_delegatee, delegate_balance)) => { - ensure!(¤t_delegatee.clone() == delegatee, Error::::InvalidDelegation); - ensure!(*delegate_balance >= value, Error::::InvalidDelegation); - - delegate_balance.saturating_reduce(value); - - if *delegate_balance == BalanceOf::::zero() { - *maybe_delegate = None; - } - Ok(()) - }, - None => { - // this should never happen - return Err(Error::::InvalidDelegation) - }, - })?; - - >::mutate(delegatee, |maybe_register| match maybe_register { - Some(ledger) => { - ledger.balance.saturating_reduce(value); - Ok(()) - }, - None => { - // this should never happen - return Err(Error::::InvalidDelegation) - }, - })?; + ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - let released = T::Currency::release( - &HoldReason::Delegating.into(), - &delegator, - value, - Precision::BestEffort, - )?; + // ensure delegatee exists. + ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); - defensive_assert!(released == value, "hold should have been released fully"); + // remove delegation of `value` from `existing_delegator`. + Self::withdraw(existing_delegator, delegatee, value)?; - Ok(()) - } + // transfer the withdrawn value to `new_delegator`. + T::Currency::transfer(existing_delegator, new_delegator, value, Preservation::Expendable) + .map_err(|_| Error::::BadState)?; - fn delegator_migrate( - delegator_from: &Self::AccountId, - delegator_to: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> sp_runtime::DispatchResult { - todo!() + // add the above removed delegation to `new_delegator`. + Self::delegate(new_delegator, delegatee, value) } } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 7154d2c8c667..6ee556bbcb75 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -17,23 +17,21 @@ use codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime::{ - DispatchResult, Saturating, -}; -use sp_std::{ops::Sub}; +use sp_runtime::{DispatchResult, Saturating}; +use sp_std::ops::Sub; /// Allows an account to accept stake delegations and manage its operations. pub trait Delegatee { /// Balance type used by the staking system. type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; @@ -45,9 +43,6 @@ pub trait Delegatee { fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> DispatchResult; /// Stop accepting new delegations on this account. - /// - /// The account would continue to be a delegatee until all delegations to this account has been - /// withdrawn. fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; /// Remove oneself as Delegatee. @@ -58,6 +53,13 @@ pub trait Delegatee { /// Update bond whenever there is a new delegate funds that are not staked. fn update_bond(delegatee: &Self::AccountId) -> DispatchResult; + /// Request removal of delegated stake. + fn withdraw( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult; + /// Applies a pending slash on delegatee by passing a delegator account who should be slashed /// and the value to be slashed. Optionally also takes a reporter account who will be rewarded /// from part of the slash imbalance. @@ -83,20 +85,19 @@ pub trait Delegatee { proxy_delegator: &Self::AccountId, payee: &Self::AccountId, ) -> DispatchResult; - } /// Allows an account to delegate their stakes to a delegatee. pub trait Delegator { type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; @@ -115,13 +116,6 @@ pub trait Delegator { value: Self::Balance, ) -> DispatchResult; - /// Request removal of delegated stake. - fn withdraw ( - delegator: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult; - /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining /// the same. /// @@ -135,4 +129,4 @@ pub trait Delegator { delegatee: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; -} \ No newline at end of file +} From 666c50abdde75288a111c415471698fd9b5423a1 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 17:43:48 +0100 Subject: [PATCH 009/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index e3a1b48df450..2e3a9ecc7c24 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -23,15 +23,13 @@ use frame_support::{ traits::{ fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, tokens::{Fortitude, Precision, Preservation}, - ExistenceRequirement, }, }; -use frame_system::pallet_prelude::*; use pallet::*; -use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug, Saturating}; +use sp_runtime::{traits::Zero, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{Delegatee, Delegator}, - EraIndex, Stake, StakerStatus, StakingInterface, + StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; From ba5b06e3779ad80c208b870edc1e13e2d1c9f33f Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 25 Nov 2023 19:04:45 +0100 Subject: [PATCH 010/202] bootstrap mock --- Cargo.lock | 7 + substrate/frame/delegated-staking/Cargo.toml | 19 +++ .../delegated-staking/src/benchmarking.rs | 20 +++ substrate/frame/delegated-staking/src/lib.rs | 9 ++ substrate/frame/delegated-staking/src/mock.rs | 151 ++++++++++++++++++ .../frame/delegated-staking/src/tests.rs | 18 +++ substrate/frame/staking/src/pallet/impls.rs | 2 +- 7 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 substrate/frame/delegated-staking/src/benchmarking.rs create mode 100644 substrate/frame/delegated-staking/src/mock.rs create mode 100644 substrate/frame/delegated-staking/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index fb275c898890..b6356a491be6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9680,8 +9680,13 @@ dependencies = [ name = "pallet-delegated-staking" version = "4.0.0-dev" dependencies = [ + "frame-election-provider-support", "frame-support", "frame-system", + "pallet-balances", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-timestamp", "parity-scale-codec", "scale-info", "sp-core", @@ -9689,6 +9694,8 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-std 8.0.0", + "sp-tracing 10.0.0", + "substrate-test-utils", ] [[package]] diff --git a/substrate/frame/delegated-staking/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml index cb2d571844ea..735b2794d5ea 100644 --- a/substrate/frame/delegated-staking/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -23,6 +23,13 @@ sp-staking = { path = "../../primitives/staking", default-features = false } [dev-dependencies] sp-core = { path = "../../primitives/core" } sp-io = { path = "../../primitives/io" } +substrate-test-utils = { path = "../../test-utils" } +sp-tracing = { path = "../../primitives/tracing" } +pallet-staking = { path = "../staking" } +pallet-balances = { path = "../balances" } +pallet-timestamp = { path = "../timestamp" } +pallet-staking-reward-curve = { path = "../staking/reward-curve" } +frame-election-provider-support = { path = "../election-provider-support", default-features = false} [features] default = [ "std" ] @@ -36,15 +43,27 @@ std = [ "sp-std/std", "sp-runtime/std", "sp-staking/std", + "pallet-balances/std", + "pallet-staking/std", + "pallet-timestamp/std", + "frame-election-provider-support/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-staking/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "frame-election-provider-support/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime", + "pallet-balances/try-runtime", + "pallet-staking/try-runtime", + "pallet-timestamp/try-runtime", + "frame-election-provider-support/try-runtime", ] diff --git a/substrate/frame/delegated-staking/src/benchmarking.rs b/substrate/frame/delegated-staking/src/benchmarking.rs new file mode 100644 index 000000000000..eeae5fa82e64 --- /dev/null +++ b/substrate/frame/delegated-staking/src/benchmarking.rs @@ -0,0 +1,20 @@ +// This file is part of Substrate. + +// Copyright (C) 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. + +//! Benchmarking for pallet-delegated-staking. + +#![cfg(feature = "runtime-benchmarks")] \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 2e3a9ecc7c24..3f3055fa0129 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -18,6 +18,15 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; use frame_support::{ pallet_prelude::*, traits::{ diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs new file mode 100644 index 000000000000..74328a14e3e1 --- /dev/null +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -0,0 +1,151 @@ +// This file is part of Substrate. + +// Copyright (C) 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 crate::{self as delegated_staking}; +use frame_support::{ + assert_ok, derive_impl, + pallet_prelude::*, + parameter_types, + traits::{ConstU64, Currency}, + weights::constants::WEIGHT_REF_TIME_PER_SECOND, +}; + +use sp_runtime::{ + traits::{Convert, IdentityLookup}, + BuildStorage, +}; + +use frame_election_provider_support::{ + bounds::{ElectionBounds, ElectionBoundsBuilder}, + onchain, SequentialPhragmen, +}; + +pub type T = Runtime; +type Block = frame_system::mocking::MockBlock; +pub type AccountId = u64; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type Lookup = IdentityLookup; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<5>; + type WeightInfo = (); +} + +pub type Balance = u128; + +parameter_types! { + pub static ExistentialDeposit: Balance = 1; +} +impl pallet_balances::Config for Runtime { + type MaxLocks = ConstU32<128>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = (); + type MaxHolds = ConstU32<1>; +} + +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; + pub static BondingDuration: u32 = 3; + pub static CurrentEra: u32 = 0; + pub static ElectionsBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build(); +} +pub struct OnChainSeqPhragmen; +impl onchain::Config for OnChainSeqPhragmen { + type System = Runtime; + type Solver = SequentialPhragmen; + type DataProvider = Staking; + type WeightInfo = (); + type MaxWinners = ConstU32<100>; + type Bounds = ElectionsBoundsOnChain; +} + +impl pallet_staking::Config for Runtime { + type Currency = Balances; + type CurrencyBalance = Balance; + type UnixTime = pallet_timestamp::Pallet; + type CurrencyToVote = (); + type RewardRemainder = (); + type RuntimeEvent = RuntimeEvent; + type Slash = (); + type Reward = (); + type SessionsPerEra = (); + type SlashDeferDuration = (); + type AdminOrigin = frame_system::EnsureRoot; + type BondingDuration = BondingDuration; + type SessionInterface = (); + type EraPayout = pallet_staking::ConvertCurve; + type NextNewSession = (); + type HistoryDepth = ConstU32<84>; + type MaxExposurePageSize = ConstU32<64>; + type OffendingValidatorsThreshold = (); + type ElectionProvider = onchain::OnChainExecution; + type GenesisElectionProvider = Self::ElectionProvider; + type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; + type TargetList = pallet_staking::UseValidatorsMap; + type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; + type MaxUnlockingChunks = ConstU32<32>; + type EventListeners = (); + type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; + type WeightInfo = (); +} + +impl delegated_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type Staking = Staking; +} + + +frame_support::construct_runtime!( + pub struct Runtime { + System: frame_system, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Staking: pallet_staking, + DelegatedStaking: delegated_staking, + } +); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs new file mode 100644 index 000000000000..2654b670f631 --- /dev/null +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 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. + +//! Tests for pallet-delegated-staking. \ No newline at end of file diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 26af96dd54e0..5759650b4140 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -27,7 +27,7 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Currency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, + Currency, LockableCurrency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, OnUnbalanced, TryCollect, UnixTime, }, weights::Weight, From db7ede25e89bbe1b2f0c9c4b58fab43f35a3533f Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 2 Dec 2023 18:11:33 +0100 Subject: [PATCH 011/202] first test --- substrate/frame/delegated-staking/src/mock.rs | 34 +++++++++++++++++++ .../frame/delegated-staking/src/tests.rs | 12 ++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 74328a14e3e1..57828f1c8205 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -149,3 +149,37 @@ frame_support::construct_runtime!( DelegatedStaking: delegated_staking, } ); + +pub struct ExtBuilder { +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self {} + } +} + +impl ExtBuilder { + + fn build(self) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + let mut ext = sp_io::TestExternalities::from(storage); + + ext.execute_with(|| { + // for events to be deposited. + frame_system::Pallet::::set_block_number(1); + }); + + ext + } + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + sp_tracing::try_init_simple(); + let mut ext = self.build(); + ext.execute_with(test); + ext.execute_with(|| { + // DelegatedStaking::do_try_state().unwrap(); + }); + } +} \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 2654b670f631..fb86ac1394c8 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -15,4 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests for pallet-delegated-staking. \ No newline at end of file +//! Tests for pallet-delegated-staking. + +use super::*; +use crate::{mock::*, Event}; +#[test] +fn hello_world_test() { + ExtBuilder::default().build_and_execute(|| { + assert!(true) + }); + +} \ No newline at end of file From 7478f98e33743b5a9676824842a5ad6457a2d2d6 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 2 Dec 2023 18:14:47 +0100 Subject: [PATCH 012/202] empty genesis block --- substrate/frame/delegated-staking/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 3f3055fa0129..c7f9b4e6b34f 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -93,6 +93,17 @@ pub mod pallet { Delegating, } + #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] + pub struct GenesisConfig { + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + } + } + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { From 990f38a0b558c12ffe6ab02bf8e53eb670195733 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 2 Dec 2023 18:15:51 +0100 Subject: [PATCH 013/202] fmt --- .../delegated-staking/src/benchmarking.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 7 +- substrate/frame/delegated-staking/src/mock.rs | 192 +++++++++--------- .../frame/delegated-staking/src/tests.rs | 7 +- substrate/frame/staking/src/pallet/impls.rs | 4 +- 5 files changed, 102 insertions(+), 110 deletions(-) diff --git a/substrate/frame/delegated-staking/src/benchmarking.rs b/substrate/frame/delegated-staking/src/benchmarking.rs index eeae5fa82e64..808d19a5ce9a 100644 --- a/substrate/frame/delegated-staking/src/benchmarking.rs +++ b/substrate/frame/delegated-staking/src/benchmarking.rs @@ -17,4 +17,4 @@ //! Benchmarking for pallet-delegated-staking. -#![cfg(feature = "runtime-benchmarks")] \ No newline at end of file +#![cfg(feature = "runtime-benchmarks")] diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index c7f9b4e6b34f..50d0085e466e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -18,7 +18,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] - #[cfg(test)] mod mock; @@ -95,13 +94,11 @@ pub mod pallet { #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig { - } + pub struct GenesisConfig {} #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { - fn build(&self) { - } + fn build(&self) {} } #[pallet::event] diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 57828f1c8205..045c93e0c020 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -17,21 +17,21 @@ use crate::{self as delegated_staking}; use frame_support::{ - assert_ok, derive_impl, - pallet_prelude::*, - parameter_types, - traits::{ConstU64, Currency}, - weights::constants::WEIGHT_REF_TIME_PER_SECOND, + assert_ok, derive_impl, + pallet_prelude::*, + parameter_types, + traits::{ConstU64, Currency}, + weights::constants::WEIGHT_REF_TIME_PER_SECOND, }; use sp_runtime::{ - traits::{Convert, IdentityLookup}, - BuildStorage, + traits::{Convert, IdentityLookup}, + BuildStorage, }; use frame_election_provider_support::{ - bounds::{ElectionBounds, ElectionBoundsBuilder}, - onchain, SequentialPhragmen, + bounds::{ElectionBounds, ElectionBoundsBuilder}, + onchain, SequentialPhragmen, }; pub type T = Runtime; @@ -40,17 +40,17 @@ pub type AccountId = u64; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { - type Block = Block; - type AccountData = pallet_balances::AccountData; - type AccountId = AccountId; - type Lookup = IdentityLookup; + type Block = Block; + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type Lookup = IdentityLookup; } impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<5>; - type WeightInfo = (); + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<5>; + type WeightInfo = (); } pub type Balance = u128; @@ -59,20 +59,20 @@ parameter_types! { pub static ExistentialDeposit: Balance = 1; } impl pallet_balances::Config for Runtime { - type MaxLocks = ConstU32<128>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = (); - type MaxHolds = ConstU32<1>; + type MaxLocks = ConstU32<128>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = (); + type MaxHolds = ConstU32<1>; } pallet_staking_reward_curve::build! { @@ -90,56 +90,55 @@ parameter_types! { pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS; pub static BondingDuration: u32 = 3; pub static CurrentEra: u32 = 0; - pub static ElectionsBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build(); + pub static ElectionsBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build(); } pub struct OnChainSeqPhragmen; impl onchain::Config for OnChainSeqPhragmen { - type System = Runtime; - type Solver = SequentialPhragmen; - type DataProvider = Staking; - type WeightInfo = (); - type MaxWinners = ConstU32<100>; - type Bounds = ElectionsBoundsOnChain; + type System = Runtime; + type Solver = SequentialPhragmen; + type DataProvider = Staking; + type WeightInfo = (); + type MaxWinners = ConstU32<100>; + type Bounds = ElectionsBoundsOnChain; } impl pallet_staking::Config for Runtime { - type Currency = Balances; - type CurrencyBalance = Balance; - type UnixTime = pallet_timestamp::Pallet; - type CurrencyToVote = (); - type RewardRemainder = (); - type RuntimeEvent = RuntimeEvent; - type Slash = (); - type Reward = (); - type SessionsPerEra = (); - type SlashDeferDuration = (); - type AdminOrigin = frame_system::EnsureRoot; - type BondingDuration = BondingDuration; - type SessionInterface = (); - type EraPayout = pallet_staking::ConvertCurve; - type NextNewSession = (); - type HistoryDepth = ConstU32<84>; - type MaxExposurePageSize = ConstU32<64>; - type OffendingValidatorsThreshold = (); - type ElectionProvider = onchain::OnChainExecution; - type GenesisElectionProvider = Self::ElectionProvider; - type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; - type TargetList = pallet_staking::UseValidatorsMap; - type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; - type MaxUnlockingChunks = ConstU32<32>; - type EventListeners = (); - type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; - type WeightInfo = (); + type Currency = Balances; + type CurrencyBalance = Balance; + type UnixTime = pallet_timestamp::Pallet; + type CurrencyToVote = (); + type RewardRemainder = (); + type RuntimeEvent = RuntimeEvent; + type Slash = (); + type Reward = (); + type SessionsPerEra = (); + type SlashDeferDuration = (); + type AdminOrigin = frame_system::EnsureRoot; + type BondingDuration = BondingDuration; + type SessionInterface = (); + type EraPayout = pallet_staking::ConvertCurve; + type NextNewSession = (); + type HistoryDepth = ConstU32<84>; + type MaxExposurePageSize = ConstU32<64>; + type OffendingValidatorsThreshold = (); + type ElectionProvider = onchain::OnChainExecution; + type GenesisElectionProvider = Self::ElectionProvider; + type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; + type TargetList = pallet_staking::UseValidatorsMap; + type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; + type MaxUnlockingChunks = ConstU32<32>; + type EventListeners = (); + type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; + type WeightInfo = (); } impl delegated_staking::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type Staking = Staking; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type Staking = Staking; } - frame_support::construct_runtime!( pub struct Runtime { System: frame_system, @@ -150,36 +149,35 @@ frame_support::construct_runtime!( } ); -pub struct ExtBuilder { -} +pub struct ExtBuilder {} impl Default for ExtBuilder { - fn default() -> Self { - Self {} - } + fn default() -> Self { + Self {} + } } impl ExtBuilder { + fn build(self) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let mut storage = + frame_system::GenesisConfig::::default().build_storage().unwrap(); + + let mut ext = sp_io::TestExternalities::from(storage); - fn build(self) -> sp_io::TestExternalities { - sp_tracing::try_init_simple(); - let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - let mut ext = sp_io::TestExternalities::from(storage); - - ext.execute_with(|| { - // for events to be deposited. - frame_system::Pallet::::set_block_number(1); - }); - - ext - } - pub fn build_and_execute(self, test: impl FnOnce() -> ()) { - sp_tracing::try_init_simple(); - let mut ext = self.build(); - ext.execute_with(test); - ext.execute_with(|| { - // DelegatedStaking::do_try_state().unwrap(); - }); - } -} \ No newline at end of file + ext.execute_with(|| { + // for events to be deposited. + frame_system::Pallet::::set_block_number(1); + }); + + ext + } + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + sp_tracing::try_init_simple(); + let mut ext = self.build(); + ext.execute_with(test); + ext.execute_with(|| { + // DelegatedStaking::do_try_state().unwrap(); + }); + } +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index fb86ac1394c8..ebb9009bec70 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -21,8 +21,5 @@ use super::*; use crate::{mock::*, Event}; #[test] fn hello_world_test() { - ExtBuilder::default().build_and_execute(|| { - assert!(true) - }); - -} \ No newline at end of file + ExtBuilder::default().build_and_execute(|| assert!(true)); +} diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 5759650b4140..9c20e96a50c6 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -27,8 +27,8 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Currency, LockableCurrency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, - OnUnbalanced, TryCollect, UnixTime, + Currency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, + LockableCurrency, OnUnbalanced, TryCollect, UnixTime, }, weights::Weight, }; From 11959190b30b47452156b108e4166b88b3fc4000 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 2 Dec 2023 18:37:50 +0100 Subject: [PATCH 014/202] empty tests and stake balance provider --- substrate/frame/delegated-staking/src/lib.rs | 35 ++++++++++++----- .../frame/delegated-staking/src/tests.rs | 39 ++++++++++++++++++- substrate/primitives/staking/src/lib.rs | 21 ++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 50d0085e466e..33391a1edeaf 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -34,7 +34,7 @@ use frame_support::{ }, }; use pallet::*; -use sp_runtime::{traits::Zero, RuntimeDebug, Saturating}; +use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{Delegatee, Delegator}, StakerStatus, StakingInterface, @@ -92,14 +92,14 @@ pub mod pallet { Delegating, } - #[pallet::genesis_config] - #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig {} - - #[pallet::genesis_build] - impl BuildGenesisConfig for GenesisConfig { - fn build(&self) {} - } + // #[pallet::genesis_config] + // #[derive(frame_support::DefaultNoBound)] + // pub struct GenesisConfig {} + // + // #[pallet::genesis_build] + // impl BuildGenesisConfig for GenesisConfig { + // fn build(&self) {} + // } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -392,3 +392,20 @@ impl Delegator for Pallet { Self::delegate(new_delegator, delegatee, value) } } + +impl sp_staking::StakeBalanceProvider for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn stakeable_balance(who: Self::AccountId) -> Self::Balance { + todo!() + } + + fn hold_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult { + todo!() + } + + fn release_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult { + todo!() + } +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index ebb9009bec70..c9690de90976 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -20,6 +20,43 @@ use super::*; use crate::{mock::*, Event}; #[test] -fn hello_world_test() { +fn create_a_delegatee_with_first_delegator() { + // Similar to creating a nomination pool + ExtBuilder::default().build_and_execute(|| assert!(true)); +} + +#[test] +fn add_delegation_to_existing_delegator() { + // Similar to creating a nomination pool + ExtBuilder::default().build_and_execute(|| assert!(true)); +} + +#[test] +fn create_multiple_delegators() { + // Similar to creating a nomination pool + ExtBuilder::default().build_and_execute(|| assert!(true)); +} + +#[test] +fn withdraw_delegation() { + // Similar to creating a nomination pool + ExtBuilder::default().build_and_execute(|| assert!(true)); +} + +#[test] +fn apply_pending_slash() { + // Similar to creating a nomination pool + ExtBuilder::default().build_and_execute(|| assert!(true)); +} + +#[test] +fn distribute_rewards() { + // Similar to creating a nomination pool + ExtBuilder::default().build_and_execute(|| assert!(true)); +} + +#[test] +fn migrate_to_delegator() { + // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| assert!(true)); } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 9be0e338af33..bc9e41dd9e57 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -420,4 +420,25 @@ pub struct PagedExposureMetadata { pub page_count: Page, } +/// Something that provides stakeable balance and a mechanism to reserve this balance. +pub trait StakeBalanceProvider { + /// Balance type used by the staking system. + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + + fn stakeable_balance(who: Self::AccountId) -> Self::Balance; + fn hold_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult; + fn release_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult; +} + sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From beb5d1d01ac52e48ab8c63c3e8ee62d29b5a201f Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 2 Dec 2023 23:54:02 +0100 Subject: [PATCH 015/202] Staking implements StakeBalanceProvider --- substrate/frame/staking/src/ledger.rs | 16 ++++++--------- substrate/frame/staking/src/mock.rs | 2 ++ substrate/frame/staking/src/pallet/impls.rs | 22 +++++++++++++++++++-- substrate/frame/staking/src/pallet/mod.rs | 16 +++++++++++---- substrate/primitives/staking/src/lib.rs | 6 +++--- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 84bb4d167dcb..697fa9f2257a 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -31,16 +31,11 @@ //! performed through the methods exposed by the [`StakingLedger`] implementation in order to ensure //! state consistency. -use frame_support::{ - defensive, - traits::{LockableCurrency, WithdrawReasons}, -}; -use sp_staking::StakingAccount; +use frame_support::defensive; +use sp_staking::{StakeBalanceProvider, StakingAccount}; use sp_std::prelude::*; -use crate::{ - BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger, STAKING_ID, -}; +use crate::{BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger}; #[cfg(any(feature = "runtime-benchmarks", test))] use sp_runtime::traits::Zero; @@ -166,7 +161,8 @@ impl StakingLedger { return Err(Error::::NotStash) } - T::Currency::set_lock(STAKING_ID, &self.stash, self.total, WithdrawReasons::all()); + T::StakeBalanceProvider::update_hold(&self.stash, self.total) + .map_err(|_| Error::::BadState)?; Ledger::::insert( &self.controller().ok_or_else(|| { defensive!("update called on a ledger that is not bonded."); @@ -207,7 +203,7 @@ impl StakingLedger { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { - T::Currency::remove_lock(STAKING_ID, &ledger.stash); + T::StakeBalanceProvider::release(&ledger.stash); Ledger::::remove(controller); >::remove(&stash); diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 5332dbfdd5b2..e28de7932e49 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -294,6 +294,8 @@ impl OnStakingUpdate for EventListenerMock { impl crate::pallet::pallet::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; + + type StakeBalanceProvider = Staking; type UnixTime = Timestamp; type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 9c20e96a50c6..5332c0ba58d6 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -41,7 +41,7 @@ use sp_runtime::{ use sp_staking::{ currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, + EraIndex, Page, SessionIndex, Stake, StakeBalanceProvider, StakingAccount::{self, Controller, Stash}, StakingInterface, }; @@ -58,6 +58,7 @@ use super::pallet::*; #[cfg(feature = "try-runtime")] use frame_support::ensure; +use frame_support::traits::WithdrawReasons; #[cfg(any(test, feature = "try-runtime"))] use sp_runtime::TryRuntimeError; @@ -1817,11 +1818,28 @@ impl StakingInterface for Pallet { } fn force_unlock(who: &Self::AccountId) -> sp_runtime::DispatchResult { - T::Currency::remove_lock(crate::STAKING_ID, who); + T::StakeBalanceProvider::release(who); Ok(()) } } +impl StakeBalanceProvider for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::free_balance(who) + } + + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { + T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); + Ok(()) + } + + fn release(who: &Self::AccountId) { + T::Currency::remove_lock(crate::STAKING_ID, who) + } +} #[cfg(any(test, feature = "try-runtime"))] impl Pallet { pub(crate) fn do_try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index b914545a76b9..508218dcfd4f 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,7 +37,7 @@ use sp_runtime::{ }; use sp_staking::{ - EraIndex, Page, SessionIndex, + EraIndex, Page, SessionIndex, StakeBalanceProvider, StakingAccount::{self, Controller, Stash}, }; use sp_std::prelude::*; @@ -103,6 +103,14 @@ pub mod pallet { + From + TypeInfo + MaxEncodedLen; + + /// Something that provides stakeable balance of an account as well as a way to hold and + /// release this stake. + type StakeBalanceProvider: StakeBalanceProvider< + Balance = Self::CurrencyBalance, + AccountId = Self::AccountId, + >; + /// Time used for computing era duration. /// /// It is guaranteed to start being called from the first `on_finalize`. Thus value at @@ -703,7 +711,7 @@ pub mod pallet { status ); assert!( - T::Currency::free_balance(stash) >= balance, + T::StakeBalanceProvider::stakeable_balance(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::bond( @@ -936,7 +944,7 @@ pub mod pallet { frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - let stash_balance = T::Currency::free_balance(&stash); + let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::new(stash.clone(), value); @@ -972,7 +980,7 @@ pub mod pallet { let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; - let stash_balance = T::Currency::free_balance(&stash); + let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); if let Some(extra) = stash_balance.checked_sub(&ledger.total) { let extra = extra.min(max_additional); ledger.total += extra; diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index bc9e41dd9e57..33fcd6a84b24 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -436,9 +436,9 @@ pub trait StakeBalanceProvider { /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; - fn stakeable_balance(who: Self::AccountId) -> Self::Balance; - fn hold_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult; - fn release_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult; + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + fn release(who: &Self::AccountId); } sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From 46e03fdbb56e254b273d36218456f702cdf150e9 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 00:00:18 +0100 Subject: [PATCH 016/202] refactor to allow partial withdraw from unlocking chunks --- substrate/frame/staking/src/lib.rs | 42 +++++++++++++++++---- substrate/frame/staking/src/pallet/impls.rs | 2 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 41cb2a12c3a3..4e10fe787a9d 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -492,19 +492,45 @@ pub struct StakingLedger { } impl StakingLedger { - /// Remove entries from `unlocking` that are sufficiently old and reduce the - /// total by the sum of their balances. - fn consolidate_unlocked(self, current_era: EraIndex) -> Self { + /// Remove entries from `unlocking` that are sufficiently old and optionally upto a given limit. + /// Reduce the total by the unlocked amount. + fn consolidate_unlocked( + self, + current_era: EraIndex, + maybe_limit: Option>, + ) -> Self { let mut total = self.total; + let mut unlocked = BalanceOf::::zero(); + + // see if there is a limit else default to total value of the ledger which implies no limit. + let limit = maybe_limit.unwrap_or(total); + + // remove chunks that are unlocking let unlocking: BoundedVec<_, _> = self .unlocking .into_iter() - .filter(|chunk| { - if chunk.era > current_era { - true + .filter_map(|chunk| { + // keep the chunks if they are from a future era or we have unlocked upto the limit + if chunk.era > current_era || limit == unlocked { + defensive_assert!(limit >= unlocked, "unlocked should never exceed limit"); + Some(chunk) } else { - total = total.saturating_sub(chunk.value); - false + // we remove chunks that are old enough until we reach limit. + let max_unlock = limit - unlocked; + if chunk.value <= max_unlock { + // unlock all and filter out + total = total.saturating_sub(chunk.value); + unlocked = unlocked.saturating_add(chunk.value); + None + } else { + // keep the leftover amount in the chunk + total = total.saturating_sub(max_unlock); + unlocked = unlocked.saturating_add(max_unlock); + Some(UnlockChunk { + value: chunk.value.saturating_sub(max_unlock), + era: chunk.era, + }) + } } }) .collect::>() diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 5332c0ba58d6..693a50a81aee 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -125,7 +125,7 @@ impl Pallet { let mut ledger = Self::ledger(Controller(controller.clone()))?; let (stash, old_total) = (ledger.stash.clone(), ledger.total); if let Some(current_era) = Self::current_era() { - ledger = ledger.consolidate_unlocked(current_era) + ledger = ledger.consolidate_unlocked(current_era, None) } let new_total = ledger.total; From aeca6dfb9972c5be092c3801a30ed47db4bf2c26 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 00:46:24 +0100 Subject: [PATCH 017/202] add fallback balance provider to delegated staking --- substrate/frame/delegated-staking/src/lib.rs | 12 +++++++++--- substrate/frame/delegated-staking/src/mock.rs | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 33391a1edeaf..30663b6b3b75 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -46,6 +46,7 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { + use sp_staking::StakeBalanceProvider; use super::*; #[pallet::pallet] @@ -63,6 +64,11 @@ pub mod pallet { /// Core staking implementation. type Staking: StakingInterface, AccountId = Self::AccountId>; + + /// Core Staking Balance Provider. + /// + /// Fallback implementation when an account is not a delegatee. + type FallbackBalanceProvider: StakeBalanceProvider, AccountId = Self::AccountId>; } #[pallet::error] @@ -397,15 +403,15 @@ impl sp_staking::StakeBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: Self::AccountId) -> Self::Balance { + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { todo!() } - fn hold_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { todo!() } - fn release_stake(who: Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn release(who: &Self::AccountId) { todo!() } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 045c93e0c020..b28e899e337f 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -38,6 +38,10 @@ pub type T = Runtime; type Block = frame_system::mocking::MockBlock; pub type AccountId = u64; +const GENESIS_VALIDATOR: AccountId = 1; +const GENESIS_NOMINATOR_ONE: AccountId = 101; +const GENESIS_NOMINATOR_TWO: AccountId = 102; + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; @@ -105,6 +109,7 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; + type StakeBalanceProvider = DelegatedStaking; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); @@ -137,6 +142,7 @@ impl delegated_staking::Config for Runtime { type Currency = Balances; type RuntimeHoldReason = RuntimeHoldReason; type Staking = Staking; + type FallbackBalanceProvider = Staking; } frame_support::construct_runtime!( From cdbd166b231339ae836d2f4f4d1bf320ed8621ec Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 01:00:43 +0100 Subject: [PATCH 018/202] setup mocks with one validator and 2 nominators at genesis --- substrate/frame/delegated-staking/src/lib.rs | 13 +++--- substrate/frame/delegated-staking/src/mock.rs | 45 ++++++++++++++++++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 30663b6b3b75..5d22c051f822 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -46,8 +46,8 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { - use sp_staking::StakeBalanceProvider; use super::*; + use sp_staking::StakeBalanceProvider; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -68,7 +68,10 @@ pub mod pallet { /// Core Staking Balance Provider. /// /// Fallback implementation when an account is not a delegatee. - type FallbackBalanceProvider: StakeBalanceProvider, AccountId = Self::AccountId>; + type FallbackBalanceProvider: StakeBalanceProvider< + Balance = BalanceOf, + AccountId = Self::AccountId, + >; } #[pallet::error] @@ -404,14 +407,14 @@ impl sp_staking::StakeBalanceProvider for Pallet { type AccountId = T::AccountId; fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - todo!() + T::FallbackBalanceProvider::stakeable_balance(who) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - todo!() + T::FallbackBalanceProvider::update_hold(who, amount) } fn release(who: &Self::AccountId) { - todo!() + T::FallbackBalanceProvider::release(who) } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index b28e899e337f..38114d194392 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -26,7 +26,7 @@ use frame_support::{ use sp_runtime::{ traits::{Convert, IdentityLookup}, - BuildStorage, + BuildStorage, Perbill, }; use frame_election_provider_support::{ @@ -169,6 +169,49 @@ impl ExtBuilder { let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let _ = pallet_balances::GenesisConfig:: { + balances: vec![ + (GENESIS_VALIDATOR, 10000), + (GENESIS_NOMINATOR_ONE, 1000), + (GENESIS_NOMINATOR_TWO, 2000), + ], + } + .assimilate_storage(&mut storage); + + let stakers = vec![ + ( + GENESIS_VALIDATOR, + GENESIS_VALIDATOR, + 1000, + sp_staking::StakerStatus::::Validator, + ), + ( + GENESIS_NOMINATOR_ONE, + GENESIS_NOMINATOR_ONE, + 100, + sp_staking::StakerStatus::::Nominator(vec![1]), + ), + ( + GENESIS_NOMINATOR_TWO, + GENESIS_NOMINATOR_TWO, + 200, + sp_staking::StakerStatus::::Nominator(vec![1]), + ), + ]; + + let _ = pallet_staking::GenesisConfig:: { + stakers: stakers.clone(), + // ideal validator count + validator_count: 2, + minimum_validator_count: 1, + invulnerables: vec![], + slash_reward_fraction: Perbill::from_percent(10), + min_nominator_bond: ExistentialDeposit::get(), + min_validator_bond: ExistentialDeposit::get(), + ..Default::default() + } + .assimilate_storage(&mut storage); + let mut ext = sp_io::TestExternalities::from(storage); ext.execute_with(|| { From 30a790aa26c7ea06bed4e8876ccc7b364b75350d Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 01:06:44 +0100 Subject: [PATCH 019/202] do try state for pallet-delegated-staking --- substrate/frame/delegated-staking/src/lib.rs | 7 +++++++ substrate/frame/delegated-staking/src/mock.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 5d22c051f822..005be3909690 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -418,3 +418,10 @@ impl sp_staking::StakeBalanceProvider for Pallet { T::FallbackBalanceProvider::release(who) } } + +#[cfg(any(test, feature = "try-runtime"))] +impl Pallet { + pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + Ok(()) + } +} diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 38114d194392..f037601b1ecb 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -226,7 +226,7 @@ impl ExtBuilder { let mut ext = self.build(); ext.execute_with(test); ext.execute_with(|| { - // DelegatedStaking::do_try_state().unwrap(); + DelegatedStaking::do_try_state().unwrap(); }); } } From 90b3b07e050537b4c59b600ba04410f9c882af56 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 01:50:56 +0100 Subject: [PATCH 020/202] return type of stake balance --- substrate/frame/delegated-staking/src/lib.rs | 39 ++++++++++--------- substrate/frame/delegated-staking/src/mock.rs | 11 ++++-- .../frame/delegated-staking/src/tests.rs | 37 +++++++++++++++++- substrate/frame/staking/src/pallet/impls.rs | 12 ++---- substrate/frame/staking/src/pallet/mod.rs | 6 +-- .../primitives/staking/src/delegation.rs | 2 +- substrate/primitives/staking/src/lib.rs | 8 +++- 7 files changed, 76 insertions(+), 39 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 005be3909690..a6d20e83ae42 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -35,10 +35,7 @@ use frame_support::{ }; use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; -use sp_staking::{ - delegation::{Delegatee, Delegator}, - StakerStatus, StakingInterface, -}; +use sp_staking::{delegation::{Delegatee, Delegator}, StakeBalanceType, StakerStatus, StakingInterface}; use sp_std::{convert::TryInto, prelude::*}; pub type BalanceOf = @@ -78,6 +75,10 @@ pub mod pallet { pub enum Error { /// The account cannot perform this operation. NotAllowed, + /// An existing staker cannot become a delegatee. + AlreadyStaker, + /// Reward Destination cannot be delegatee account. + InvalidRewardDestination, /// Delegation conditions are not met. /// /// Possible issues are @@ -168,22 +169,22 @@ impl Delegatee for Pallet { fn accept_delegations( who: &Self::AccountId, - payee: &Self::AccountId, - ) -> sp_runtime::DispatchResult { + reward_destination: &Self::AccountId, + ) -> DispatchResult { // Existing delegatee cannot accept delegation ensure!(!>::contains_key(who), Error::::NotAllowed); // payee account cannot be same as delegatee - ensure!(payee != who, Error::::InvalidDelegation); + ensure!(reward_destination != who, Error::::InvalidRewardDestination); // if already a delegator, unblock and return success >::mutate(who, |maybe_register| { if let Some(register) = maybe_register { register.blocked = false; - register.payee = payee.clone(); + register.payee = reward_destination.clone(); } else { *maybe_register = Some(DelegationRegister { - payee: payee.clone(), + payee: reward_destination.clone(), balance: Zero::zero(), pending_slash: Zero::zero(), blocked: false, @@ -194,7 +195,7 @@ impl Delegatee for Pallet { Ok(()) } - fn block_delegations(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { + fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult { >::mutate(delegatee, |maybe_register| { if let Some(register) = maybe_register { register.blocked = true; @@ -205,11 +206,11 @@ impl Delegatee for Pallet { }) } - fn kill_delegatee(delegatee: &Self::AccountId) -> sp_runtime::DispatchResult { + fn kill_delegatee(delegatee: &Self::AccountId) -> DispatchResult { todo!() } - fn update_bond(who: &Self::AccountId) -> sp_runtime::DispatchResult { + fn update_bond(who: &Self::AccountId) -> DispatchResult { let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; let delegated_balance = delegatee.effective_balance(); @@ -229,7 +230,7 @@ impl Delegatee for Pallet { delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { Some((current_delegatee, delegate_balance)) => { ensure!(¤t_delegatee.clone() == delegatee, Error::::NotDelegatee); @@ -276,7 +277,7 @@ impl Delegatee for Pallet { delegator: &Self::AccountId, value: Self::Balance, reporter: Option, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { todo!() } @@ -286,7 +287,7 @@ impl Delegatee for Pallet { new_delegatee: &Self::AccountId, proxy_delegator: &Self::AccountId, payee: &Self::AccountId, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { ensure!(new_delegatee != proxy_delegator, Error::::InvalidDelegation); // ensure proxy delegator has at least minimum balance to keep the account alive. @@ -335,7 +336,7 @@ impl Delegator for Pallet { delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { let delegator_balance = T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); @@ -373,7 +374,7 @@ impl Delegator for Pallet { delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { todo!() } @@ -384,7 +385,7 @@ impl Delegator for Pallet { new_delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); // ensure delegatee exists. @@ -406,7 +407,7 @@ impl sp_staking::StakeBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + fn stakeable_balance(who: &Self::AccountId) -> (StakeBalanceType, Self::Balance) { T::FallbackBalanceProvider::stakeable_balance(who) } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index f037601b1ecb..77adbdc4ea78 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -21,7 +21,6 @@ use frame_support::{ pallet_prelude::*, parameter_types, traits::{ConstU64, Currency}, - weights::constants::WEIGHT_REF_TIME_PER_SECOND, }; use sp_runtime::{ @@ -38,9 +37,9 @@ pub type T = Runtime; type Block = frame_system::mocking::MockBlock; pub type AccountId = u64; -const GENESIS_VALIDATOR: AccountId = 1; -const GENESIS_NOMINATOR_ONE: AccountId = 101; -const GENESIS_NOMINATOR_TWO: AccountId = 102; +pub const GENESIS_VALIDATOR: AccountId = 1; +pub const GENESIS_NOMINATOR_ONE: AccountId = 101; +pub const GENESIS_NOMINATOR_TWO: AccountId = 102; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { @@ -230,3 +229,7 @@ impl ExtBuilder { }); } } + +pub fn fund(who: AccountId, amount: Balance) { + let _ = Balances::deposit_creating(&who, amount); +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index c9690de90976..94169442f642 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -17,17 +17,50 @@ //! Tests for pallet-delegated-staking. +use frame_support::{assert_noop, assert_ok}; +use sp_staking::{StakeBalanceType, StakeBalanceProvider}; use super::*; use crate::{mock::*, Event}; + #[test] fn create_a_delegatee_with_first_delegator() { // Similar to creating a nomination pool - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + let delegatee: AccountId = 200; + fund(delegatee, 1000); + let reward_account: AccountId = 201; + let delegator: AccountId = 202; + fund(delegator, 1000); + + // set intention to accept delegation. + assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); + + // delegate to this account + assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); + + // verify + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), (StakeBalanceType::Delegated, 100)); + + }); +} + +#[test] +fn cannot_become_delegatee() { + ExtBuilder::default().build_and_execute(|| { + // cannot set reward account same as delegatee account + assert_noop!(DelegatedStaking::accept_delegations(&100, &100), Error::::InvalidRewardDestination); + + // an existing validator cannot become delegatee + assert_noop!(DelegatedStaking::accept_delegations(&mock::GENESIS_VALIDATOR, &100), Error::::AlreadyStaker); + + // an existing nominator cannot become delegatee + assert_noop!(DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_ONE, &100), Error::::AlreadyStaker); + assert_noop!(DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_TWO, &100), Error::::AlreadyStaker); + }); } #[test] fn add_delegation_to_existing_delegator() { - // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| assert!(true)); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 693a50a81aee..7ff18a6cbd84 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -38,13 +38,7 @@ use sp_runtime::{ traits::{Bounded, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero}, Perbill, }; -use sp_staking::{ - currency_to_vote::CurrencyToVote, - offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, StakeBalanceProvider, - StakingAccount::{self, Controller, Stash}, - StakingInterface, -}; +use sp_staking::{currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, Page, SessionIndex, Stake, StakeBalanceProvider, StakingAccount::{self, Controller, Stash}, StakingInterface, StakeBalanceType}; use sp_std::prelude::*; use crate::{ @@ -1827,8 +1821,8 @@ impl StakeBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::free_balance(who) + fn stakeable_balance(who: &Self::AccountId) -> (StakeBalanceType, Self::Balance) { + (StakeBalanceType::Direct, T::Currency::free_balance(who)) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 508218dcfd4f..46afffe513fe 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -711,7 +711,7 @@ pub mod pallet { status ); assert!( - T::StakeBalanceProvider::stakeable_balance(stash) >= balance, + T::StakeBalanceProvider::stakeable_balance(stash).1 >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::bond( @@ -944,7 +944,7 @@ pub mod pallet { frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); + let (_, stash_balance) = T::StakeBalanceProvider::stakeable_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::new(stash.clone(), value); @@ -980,7 +980,7 @@ pub mod pallet { let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; - let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); + let (_, stash_balance) = T::StakeBalanceProvider::stakeable_balance(&stash); if let Some(extra) = stash_balance.checked_sub(&ledger.total) { let extra = extra.min(max_additional); ledger.total += extra; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 6ee556bbcb75..0a77a17046ff 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -40,7 +40,7 @@ pub trait Delegatee { fn delegate_balance(who: Self::AccountId) -> Self::Balance; /// Set intention to accept delegations. - fn accept_delegations(delegatee: &Self::AccountId, payee: &Self::AccountId) -> DispatchResult; + fn accept_delegations(delegatee: &Self::AccountId, reward_destination: &Self::AccountId) -> DispatchResult; /// Stop accepting new delegations on this account. fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 33fcd6a84b24..d39f5ff53f6f 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -436,9 +436,15 @@ pub trait StakeBalanceProvider { /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; + fn stakeable_balance(who: &Self::AccountId) -> (StakeBalanceType, Self::Balance); fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; fn release(who: &Self::AccountId); } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum StakeBalanceType { + Direct, + Delegated, +} + sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From c31c085e0f03c76233beee1266ddf53e25fd9057 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 02:01:26 +0100 Subject: [PATCH 021/202] failing tests --- substrate/frame/delegated-staking/src/lib.rs | 7 ++++++- substrate/frame/delegated-staking/src/tests.rs | 3 ++- substrate/frame/staking/src/pallet/impls.rs | 9 +++++++-- substrate/frame/staking/src/pallet/mod.rs | 6 +++--- substrate/primitives/staking/src/lib.rs | 5 ++++- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index a6d20e83ae42..d84cc69efafe 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -407,7 +407,7 @@ impl sp_staking::StakeBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> (StakeBalanceType, Self::Balance) { + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { T::FallbackBalanceProvider::stakeable_balance(who) } @@ -418,6 +418,11 @@ impl sp_staking::StakeBalanceProvider for Pallet { fn release(who: &Self::AccountId) { T::FallbackBalanceProvider::release(who) } + + #[cfg(feature = "std")] + fn stake_type(who: &Self::AccountId) -> StakeBalanceType { + T::FallbackBalanceProvider::stake_type(who) + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 94169442f642..73c4a09f2fae 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -39,7 +39,8 @@ fn create_a_delegatee_with_first_delegator() { assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); // verify - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), (StakeBalanceType::Delegated, 100)); + assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); }); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 7ff18a6cbd84..6185c76fbf34 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1821,8 +1821,8 @@ impl StakeBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> (StakeBalanceType, Self::Balance) { - (StakeBalanceType::Direct, T::Currency::free_balance(who)) + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::free_balance(who) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { @@ -1833,6 +1833,11 @@ impl StakeBalanceProvider for Pallet { fn release(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } + + #[cfg(feature = "std")] + fn stake_type(_: &Self::AccountId) -> StakeBalanceType { + StakeBalanceType::Direct + } } #[cfg(any(test, feature = "try-runtime"))] impl Pallet { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 46afffe513fe..508218dcfd4f 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -711,7 +711,7 @@ pub mod pallet { status ); assert!( - T::StakeBalanceProvider::stakeable_balance(stash).1 >= balance, + T::StakeBalanceProvider::stakeable_balance(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::bond( @@ -944,7 +944,7 @@ pub mod pallet { frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - let (_, stash_balance) = T::StakeBalanceProvider::stakeable_balance(&stash); + let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::new(stash.clone(), value); @@ -980,7 +980,7 @@ pub mod pallet { let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; - let (_, stash_balance) = T::StakeBalanceProvider::stakeable_balance(&stash); + let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); if let Some(extra) = stash_balance.checked_sub(&ledger.total) { let extra = extra.min(max_additional); ledger.total += extra; diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index d39f5ff53f6f..6b1cd1632943 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -436,9 +436,12 @@ pub trait StakeBalanceProvider { /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; - fn stakeable_balance(who: &Self::AccountId) -> (StakeBalanceType, Self::Balance); + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; fn release(who: &Self::AccountId); + + #[cfg(feature = "std")] + fn stake_type(who: &Self::AccountId) -> StakeBalanceType; } #[derive(Clone, Debug, Eq, PartialEq)] From 41d0ee066f86275f5848dcb4813f492cc034fd9d Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 02:01:48 +0100 Subject: [PATCH 022/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 7 ++++-- substrate/frame/delegated-staking/src/mock.rs | 2 +- .../frame/delegated-staking/src/tests.rs | 25 +++++++++++++------ substrate/frame/staking/src/pallet/impls.rs | 8 +++++- .../primitives/staking/src/delegation.rs | 5 +++- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d84cc69efafe..760bb6c73133 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -35,7 +35,10 @@ use frame_support::{ }; use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; -use sp_staking::{delegation::{Delegatee, Delegator}, StakeBalanceType, StakerStatus, StakingInterface}; +use sp_staking::{ + delegation::{Delegatee, Delegator}, + StakeBalanceType, StakerStatus, StakingInterface, +}; use sp_std::{convert::TryInto, prelude::*}; pub type BalanceOf = @@ -407,7 +410,7 @@ impl sp_staking::StakeBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { T::FallbackBalanceProvider::stakeable_balance(who) } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 77adbdc4ea78..99b9a94c68ad 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -231,5 +231,5 @@ impl ExtBuilder { } pub fn fund(who: AccountId, amount: Balance) { - let _ = Balances::deposit_creating(&who, amount); + let _ = Balances::deposit_creating(&who, amount); } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 73c4a09f2fae..7a0085f0033c 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -17,10 +17,10 @@ //! Tests for pallet-delegated-staking. -use frame_support::{assert_noop, assert_ok}; -use sp_staking::{StakeBalanceType, StakeBalanceProvider}; use super::*; use crate::{mock::*, Event}; +use frame_support::{assert_noop, assert_ok}; +use sp_staking::{StakeBalanceProvider, StakeBalanceType}; #[test] fn create_a_delegatee_with_first_delegator() { @@ -41,7 +41,6 @@ fn create_a_delegatee_with_first_delegator() { // verify assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); - }); } @@ -49,14 +48,26 @@ fn create_a_delegatee_with_first_delegator() { fn cannot_become_delegatee() { ExtBuilder::default().build_and_execute(|| { // cannot set reward account same as delegatee account - assert_noop!(DelegatedStaking::accept_delegations(&100, &100), Error::::InvalidRewardDestination); + assert_noop!( + DelegatedStaking::accept_delegations(&100, &100), + Error::::InvalidRewardDestination + ); // an existing validator cannot become delegatee - assert_noop!(DelegatedStaking::accept_delegations(&mock::GENESIS_VALIDATOR, &100), Error::::AlreadyStaker); + assert_noop!( + DelegatedStaking::accept_delegations(&mock::GENESIS_VALIDATOR, &100), + Error::::AlreadyStaker + ); // an existing nominator cannot become delegatee - assert_noop!(DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_ONE, &100), Error::::AlreadyStaker); - assert_noop!(DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_TWO, &100), Error::::AlreadyStaker); + assert_noop!( + DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_ONE, &100), + Error::::AlreadyStaker + ); + assert_noop!( + DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_TWO, &100), + Error::::AlreadyStaker + ); }); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 6185c76fbf34..c0fd4fcc9937 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -38,7 +38,13 @@ use sp_runtime::{ traits::{Bounded, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero}, Perbill, }; -use sp_staking::{currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, Page, SessionIndex, Stake, StakeBalanceProvider, StakingAccount::{self, Controller, Stash}, StakingInterface, StakeBalanceType}; +use sp_staking::{ + currency_to_vote::CurrencyToVote, + offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, + EraIndex, Page, SessionIndex, Stake, StakeBalanceProvider, StakeBalanceType, + StakingAccount::{self, Controller, Stash}, + StakingInterface, +}; use sp_std::prelude::*; use crate::{ diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 0a77a17046ff..09e27d405fe8 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -40,7 +40,10 @@ pub trait Delegatee { fn delegate_balance(who: Self::AccountId) -> Self::Balance; /// Set intention to accept delegations. - fn accept_delegations(delegatee: &Self::AccountId, reward_destination: &Self::AccountId) -> DispatchResult; + fn accept_delegations( + delegatee: &Self::AccountId, + reward_destination: &Self::AccountId, + ) -> DispatchResult; /// Stop accepting new delegations on this account. fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; From c1ad5b636a65384f64e31d1ec18d1bd09001a562 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 02:49:15 +0100 Subject: [PATCH 023/202] green tests --- substrate/frame/delegated-staking/src/lib.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 760bb6c73133..73b4432ddc92 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -177,6 +177,9 @@ impl Delegatee for Pallet { // Existing delegatee cannot accept delegation ensure!(!>::contains_key(who), Error::::NotAllowed); + // make sure they are not already a direct staker + ensure!(T::Staking::status(who).is_err(), Error::::AlreadyStaker); + // payee account cannot be same as delegatee ensure!(reward_destination != who, Error::::InvalidRewardDestination); @@ -411,7 +414,10 @@ impl sp_staking::StakeBalanceProvider for Pallet { type AccountId = T::AccountId; fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - T::FallbackBalanceProvider::stakeable_balance(who) + >::get(who).map_or_else( + || T::FallbackBalanceProvider::stakeable_balance(who), + |delegatee| delegatee.effective_balance(), + ) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { @@ -424,10 +430,20 @@ impl sp_staking::StakeBalanceProvider for Pallet { #[cfg(feature = "std")] fn stake_type(who: &Self::AccountId) -> StakeBalanceType { + if Self::is_delegatee(who) { + return StakeBalanceType::Delegated; + } + T::FallbackBalanceProvider::stake_type(who) } } +impl Pallet { + fn is_delegatee(who: &T::AccountId) -> bool { + >::contains_key(who) + } +} + #[cfg(any(test, feature = "try-runtime"))] impl Pallet { pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { From 918233ef460f17cd8a883d0e5f6650f5910f75c7 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 03:09:14 +0100 Subject: [PATCH 024/202] refactor --- substrate/frame/delegated-staking/src/lib.rs | 127 +++++++++--------- .../frame/delegated-staking/src/tests.rs | 35 ++++- .../primitives/staking/src/delegation.rs | 56 ++++---- 3 files changed, 125 insertions(+), 93 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 73b4432ddc92..493d1d7a13c7 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -201,6 +201,52 @@ impl Delegatee for Pallet { Ok(()) } + /// Transfers funds from current staked account to `proxy_delegator`. Current staked account + /// becomes a delegatee with `proxy_delegator` delegating stakes to it. + fn migrate_accept_delegations( + new_delegatee: &Self::AccountId, + proxy_delegator: &Self::AccountId, + payee: &Self::AccountId, + ) -> DispatchResult { + ensure!(new_delegatee != proxy_delegator, Error::::InvalidDelegation); + + // ensure proxy delegator has at least minimum balance to keep the account alive. + ensure!( + T::Currency::reducible_balance( + proxy_delegator, + Preservation::Expendable, + Fortitude::Polite + ) > Zero::zero(), + Error::::NotEnoughFunds + ); + + // ensure staker is a nominator + let status = T::Staking::status(new_delegatee)?; + match status { + StakerStatus::Nominator(_) => (), + _ => return Err(Error::::InvalidDelegation.into()), + } + + let stake = T::Staking::stake(new_delegatee)?; + + // unlock funds from staker + T::Staking::force_unlock(new_delegatee)?; + + // try transferring the staked amount. This should never fail but if it does, it indicates + // bad state and we abort. + T::Currency::transfer( + new_delegatee, + proxy_delegator, + stake.total, + Preservation::Expendable, + ) + .map_err(|_| Error::::BadState)?; + + // delegate from new delegator to staker. + Self::accept_delegations(new_delegatee, payee)?; + Self::delegate(proxy_delegator, new_delegatee, stake.total) + } + fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult { >::mutate(delegatee, |maybe_register| { if let Some(register) = maybe_register { @@ -287,51 +333,30 @@ impl Delegatee for Pallet { todo!() } - /// Transfers funds from current staked account to `proxy_delegator`. Current staked account - /// becomes a delegatee with `proxy_delegator` delegating stakes to it. - fn delegatee_migrate( - new_delegatee: &Self::AccountId, - proxy_delegator: &Self::AccountId, - payee: &Self::AccountId, + /// Move funds from proxy delegator to actual delegator. + // TODO: Keep track of proxy delegator and only allow movement from proxy -> new delegator + fn migrate_delegator( + delegatee: &Self::AccountId, + existing_delegator: &Self::AccountId, + new_delegator: &Self::AccountId, + value: Self::Balance, ) -> DispatchResult { - ensure!(new_delegatee != proxy_delegator, Error::::InvalidDelegation); - - // ensure proxy delegator has at least minimum balance to keep the account alive. - ensure!( - T::Currency::reducible_balance( - proxy_delegator, - Preservation::Expendable, - Fortitude::Polite - ) > Zero::zero(), - Error::::NotEnoughFunds - ); - - // ensure staker is a nominator - let status = T::Staking::status(new_delegatee)?; - match status { - StakerStatus::Nominator(_) => (), - _ => return Err(Error::::InvalidDelegation.into()), - } + ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - let stake = T::Staking::stake(new_delegatee)?; + // ensure delegatee exists. + ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); - // unlock funds from staker - T::Staking::force_unlock(new_delegatee)?; + // remove delegation of `value` from `existing_delegator`. + Self::withdraw(existing_delegator, delegatee, value)?; - // try transferring the staked amount. This should never fail but if it does, it indicates - // bad state and we abort. - T::Currency::transfer( - new_delegatee, - proxy_delegator, - stake.total, - Preservation::Expendable, - ) - .map_err(|_| Error::::BadState)?; + // transfer the withdrawn value to `new_delegator`. + T::Currency::transfer(existing_delegator, new_delegator, value, Preservation::Expendable) + .map_err(|_| Error::::BadState)?; - // delegate from new delegator to staker. - Self::accept_delegations(new_delegatee, payee)?; - Self::delegate(proxy_delegator, new_delegatee, stake.total) + // add the above removed delegation to `new_delegator`. + Self::delegate(new_delegator, delegatee, value) } + } impl Delegator for Pallet { @@ -383,30 +408,6 @@ impl Delegator for Pallet { ) -> DispatchResult { todo!() } - - /// Move funds from proxy delegator to actual delegator. - // TODO: Keep track of proxy delegator and only allow movement from proxy -> new delegator - fn delegator_migrate( - existing_delegator: &Self::AccountId, - new_delegator: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult { - ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - - // ensure delegatee exists. - ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); - - // remove delegation of `value` from `existing_delegator`. - Self::withdraw(existing_delegator, delegatee, value)?; - - // transfer the withdrawn value to `new_delegator`. - T::Currency::transfer(existing_delegator, new_delegator, value, Preservation::Expendable) - .map_err(|_| Error::::BadState)?; - - // add the above removed delegation to `new_delegator`. - Self::delegate(new_delegator, delegatee, value) - } } impl sp_staking::StakeBalanceProvider for Pallet { diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 7a0085f0033c..ebe1dc05da9c 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -24,7 +24,6 @@ use sp_staking::{StakeBalanceProvider, StakeBalanceType}; #[test] fn create_a_delegatee_with_first_delegator() { - // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| { let delegatee: AccountId = 200; fund(delegatee, 1000); @@ -90,18 +89,46 @@ fn withdraw_delegation() { #[test] fn apply_pending_slash() { - // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| assert!(true)); } #[test] fn distribute_rewards() { - // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| assert!(true)); } #[test] fn migrate_to_delegator() { - // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| assert!(true)); } + +/// Integration tests with pallet-staking and pallet-nomination-pools. +mod integration { + use crate::mock::ExtBuilder; + + #[test] + fn bond() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } + + #[test] + fn bond_extra() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } + + #[test] + fn partial_withdraw() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } + + #[test] + fn claim_reward() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } + + #[test] + fn slash_works() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } + +} \ No newline at end of file diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 09e27d405fe8..71e756e47cfd 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -45,6 +45,25 @@ pub trait Delegatee { reward_destination: &Self::AccountId, ) -> DispatchResult; + /// Migrate an nominator account into a delegatee. + /// + /// # Arguments + /// + /// * `new_delegatee`: This is the current nominator account. Funds will be moved from this + /// account to `proxy_delegator` and delegated back to `new_delegatee`. + /// * `proxy_delegator`: All existing staked funds will be moved to this account. Future + /// migration of funds from `proxy_delegator` to `delegator` is possible via calling + /// [`Self::migrate_delegator`]. + /// * `payee`: Delegatees need to set where they want their rewards to be paid out. + /// + /// This is similar to [`Self::accept_delegations`] but allows a current nominator to migrate to + /// a delegatee. + fn migrate_accept_delegations( + new_delegatee: &Self::AccountId, + proxy_delegator: &Self::AccountId, + payee: &Self::AccountId, + ) -> DispatchResult; + /// Stop accepting new delegations on this account. fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; @@ -73,20 +92,19 @@ pub trait Delegatee { reporter: Option, ) -> DispatchResult; - /// Migrate a nominator account into a delegatee by moving its funds to delegator account and - /// delegating these funds back to delegatee. - /// - /// Also takes input a payee which will be the new reward destination for the new delegatee. + /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining + /// the same. /// - /// This is useful for migrating old pool accounts to use delegation by providing a pool - /// delegator account. This pool delegator account funds can then lazily move funds to actual - /// delegators using [`Self::delegator_migrate`]. + /// This is useful for migrating old pool accounts using direct staking to lazily move + /// delegators to the new delegated pool account. /// - /// Note: Potentially unsafe and should be only called by trusted runtime code. - fn delegatee_migrate( - new_delegatee: &Self::AccountId, - proxy_delegator: &Self::AccountId, - payee: &Self::AccountId, + /// FIXME(ank4n): delegator_from should be removed and be always `proxy_delegator` that was + /// registered while calling [`Self::migrate_accept_delegations`]. + fn migrate_delegator( + delegatee: &Self::AccountId, + delegator_from: &Self::AccountId, + delegator_to: &Self::AccountId, + value: Self::Balance, ) -> DispatchResult; } @@ -118,18 +136,4 @@ pub trait Delegator { delegatee: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; - - /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining - /// the same. - /// - /// This is useful for migrating old pool accounts using direct staking to lazily move - /// delegators to the new delegated pool account. - /// - /// Note: Potentially unsafe and should be only called by trusted runtime code. - fn delegator_migrate( - delegator_from: &Self::AccountId, - delegator_to: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult; } From 883d62c33b50f4d24270dc1fc39ddbfe2c181a22 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 03:12:02 +0100 Subject: [PATCH 025/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 1 - substrate/frame/delegated-staking/src/tests.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 493d1d7a13c7..49fb58d3779b 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -356,7 +356,6 @@ impl Delegatee for Pallet { // add the above removed delegation to `new_delegator`. Self::delegate(new_delegator, delegatee, value) } - } impl Delegator for Pallet { diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index ebe1dc05da9c..c026c3e49109 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -130,5 +130,4 @@ mod integration { fn slash_works() { ExtBuilder::default().build_and_execute(|| assert!(true)); } - -} \ No newline at end of file +} From 26da4ceb9d1501152bd9529c4597761e5ee26f88 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 03:23:29 +0100 Subject: [PATCH 026/202] green create_multiple_delegators test --- .../frame/delegated-staking/src/tests.rs | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index c026c3e49109..40c30c9ce038 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -70,15 +70,30 @@ fn cannot_become_delegatee() { }); } -#[test] -fn add_delegation_to_existing_delegator() { - ExtBuilder::default().build_and_execute(|| assert!(true)); -} - #[test] fn create_multiple_delegators() { - // Similar to creating a nomination pool - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + let delegatee: AccountId = 200; + fund(delegatee, 1000); + let reward_account: AccountId = 201; + + // before becoming a delegatee, stakeable balance is only direct balance. + assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Direct); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 1000); + + // set intention to accept delegation. + assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); + + // create 100 delegators + for i in 202..302 { + fund(i, 100 + ExistentialDeposit::get()); + assert_ok!(DelegatedStaking::delegate(&i, &delegatee, 100)); + } + + // verify + assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100 * 100); + }); } #[test] From 215718df65c26ae968fb0432b4f4adf6c699c870 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 16:32:24 +0100 Subject: [PATCH 027/202] Reward destincation checker --- substrate/frame/delegated-staking/src/lib.rs | 6 +-- substrate/frame/delegated-staking/src/mock.rs | 4 +- .../frame/delegated-staking/src/tests.rs | 48 +++++++++++++----- substrate/frame/staking/src/ledger.rs | 49 +++++++------------ substrate/frame/staking/src/lib.rs | 16 ++++++ substrate/frame/staking/src/mock.rs | 2 + substrate/frame/staking/src/pallet/impls.rs | 4 +- substrate/frame/staking/src/pallet/mod.rs | 8 ++- substrate/primitives/staking/src/lib.rs | 37 ++++++++------ 9 files changed, 110 insertions(+), 64 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 49fb58d3779b..c52f93448f5a 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -47,7 +47,7 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { use super::*; - use sp_staking::StakeBalanceProvider; + use sp_staking::StakingBalanceProvider; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -68,7 +68,7 @@ pub mod pallet { /// Core Staking Balance Provider. /// /// Fallback implementation when an account is not a delegatee. - type FallbackBalanceProvider: StakeBalanceProvider< + type FallbackBalanceProvider: StakingBalanceProvider< Balance = BalanceOf, AccountId = Self::AccountId, >; @@ -409,7 +409,7 @@ impl Delegator for Pallet { } } -impl sp_staking::StakeBalanceProvider for Pallet { +impl sp_staking::StakingBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 99b9a94c68ad..e136ee56fa3a 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -230,6 +230,8 @@ impl ExtBuilder { } } -pub fn fund(who: AccountId, amount: Balance) { +/// fund and return who. +pub fn fund(who: AccountId, amount: Balance) -> AccountId { let _ = Balances::deposit_creating(&who, amount); + who } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 40c30c9ce038..0713261b5394 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -20,26 +20,27 @@ use super::*; use crate::{mock::*, Event}; use frame_support::{assert_noop, assert_ok}; -use sp_staking::{StakeBalanceProvider, StakeBalanceType}; +use pallet_staking::Error as StakingError; +use sp_staking::{StakingBalanceProvider, StakeBalanceType}; +use frame_support::traits::fungible::InspectHold; #[test] fn create_a_delegatee_with_first_delegator() { ExtBuilder::default().build_and_execute(|| { let delegatee: AccountId = 200; - fund(delegatee, 1000); let reward_account: AccountId = 201; let delegator: AccountId = 202; - fund(delegator, 1000); // set intention to accept delegation. - assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); + assert_ok!(DelegatedStaking::accept_delegations(&fund(delegatee, 1000), &reward_account)); // delegate to this account - assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(&fund(delegator, 1000), &delegatee, 100)); // verify assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); + assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100); }); } @@ -74,11 +75,10 @@ fn cannot_become_delegatee() { fn create_multiple_delegators() { ExtBuilder::default().build_and_execute(|| { let delegatee: AccountId = 200; - fund(delegatee, 1000); let reward_account: AccountId = 201; // before becoming a delegatee, stakeable balance is only direct balance. - assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Direct); + assert_eq!(DelegatedStaking::stake_type(&fund(delegatee, 1000)), StakeBalanceType::Direct); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 1000); // set intention to accept delegation. @@ -86,8 +86,13 @@ fn create_multiple_delegators() { // create 100 delegators for i in 202..302 { - fund(i, 100 + ExistentialDeposit::get()); - assert_ok!(DelegatedStaking::delegate(&i, &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate( + &fund(i, 100 + ExistentialDeposit::get()), + &delegatee, + 100 + )); + // Balance of 100 held on delegator account for delegating to the delegatee. + assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &i), 100); } // verify @@ -117,13 +122,32 @@ fn migrate_to_delegator() { ExtBuilder::default().build_and_execute(|| assert!(true)); } -/// Integration tests with pallet-staking and pallet-nomination-pools. +/// Integration tests with pallet-staking. mod integration { - use crate::mock::ExtBuilder; + use super::*; #[test] fn bond() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + let delegatee: AccountId = 99; + let reward_acc: AccountId = 100; + assert_eq!(Staking::status(&delegatee), Err(StakingError::::NotStash.into())); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); + assert_eq!(Balances::free_balance(delegatee), 0); + + // set intention to become a delegatee + assert_ok!(DelegatedStaking::accept_delegations(&fund(delegatee, 100), &reward_acc)); + // set some delegations + for delegator in 200..250 { + assert_ok!(DelegatedStaking::delegate(&fund(delegator, 1000), &delegatee, 100)); + assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); + + assert_ok!(Staking::bond(RuntimeOrigin::signed(delegatee), )); + } + + // + }); } #[test] diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 697fa9f2257a..a9d46b098aa2 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -16,26 +16,13 @@ // limitations under the License. //! A Ledger implementation for stakers. -//! -//! A [`StakingLedger`] encapsulates all the state and logic related to the stake of bonded -//! stakers, namely, it handles the following storage items: -//! * [`Bonded`]: mutates and reads the state of the controller <> stash bond map (to be deprecated -//! soon); -//! * [`Ledger`]: mutates and reads the state of all the stakers. The [`Ledger`] storage item stores -//! instances of [`StakingLedger`] keyed by the staker's controller account and should be mutated -//! and read through the [`StakingLedger`] API; -//! * [`Payee`]: mutates and reads the reward destination preferences for a bonded stash. -//! * Staking locks: mutates the locks for staking. -//! -//! NOTE: All the storage operations related to the staking ledger (both reads and writes) *MUST* be -//! performed through the methods exposed by the [`StakingLedger`] implementation in order to ensure -//! state consistency. use frame_support::defensive; -use sp_staking::{StakeBalanceProvider, StakingAccount}; +use sp_staking::{StakingAccount, StakingBalanceProvider}; use sp_std::prelude::*; use crate::{BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger}; +use sp_staking::RewardDestinationChecker; #[cfg(any(feature = "runtime-benchmarks", test))] use sp_runtime::traits::Zero; @@ -54,12 +41,6 @@ impl StakingLedger { } /// Returns a new instance of a staking ledger. - /// - /// The [`Ledger`] storage is not mutated. In order to store, `StakingLedger::update` must be - /// called on the returned staking ledger. - /// - /// Note: as the controller accounts are being deprecated, the stash account is the same as the - /// controller account. pub fn new(stash: T::AccountId, stake: BalanceOf) -> Self { Self { stash: stash.clone(), @@ -179,22 +160,30 @@ impl StakingLedger { /// It sets the reward preferences for the bonded stash. pub(crate) fn bond(self, payee: RewardDestination) -> Result<(), Error> { if >::contains_key(&self.stash) { - Err(Error::::AlreadyBonded) - } else { - >::insert(&self.stash, payee); - >::insert(&self.stash, &self.stash); - self.update() + return Err(Error::::AlreadyBonded); } + + if T::RewardDestinationChecker::restrict(&self.stash, payee.clone().from(&self.stash)) { + return Err(Error::::RewardDestinationRestricted); + } + + >::insert(&self.stash, payee); + >::insert(&self.stash, &self.stash); + self.update() } /// Sets the ledger Payee. pub(crate) fn set_payee(self, payee: RewardDestination) -> Result<(), Error> { if !>::contains_key(&self.stash) { - Err(Error::::NotStash) - } else { - >::insert(&self.stash, payee); - Ok(()) + return Err(Error::::NotStash); + } + + if T::RewardDestinationChecker::restrict(&self.stash, payee.clone().from(&self.stash)) { + return Err(Error::::RewardDestinationRestricted); } + + >::insert(&self.stash, payee); + Ok(()) } /// Clears all data related to a staking ledger and its bond in both [`Ledger`] and [`Bonded`] diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 4e10fe787a9d..97398e457cc0 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -413,6 +413,22 @@ impl Default for RewardDestination { } } +impl RewardDestination { + fn from(self, stash: &AccountId) -> Option { + match self { + // FIXME(ank4n): Figure out later how to handle Controller + RewardDestination::Staked | RewardDestination::Stash => Some(stash.clone()), + RewardDestination::Account(a) => Some(a), + #[allow(deprecated)] + _ => { + defensive!("reward destination not set or set as deprecated controller"); + None + }, + } + } + +} + /// Preference of what happens regarding validation. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default, MaxEncodedLen)] pub struct ValidatorPrefs { diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index e28de7932e49..eb7f9cb9e60f 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -296,6 +296,8 @@ impl crate::pallet::pallet::Config for Test { type CurrencyBalance = ::Balance; type StakeBalanceProvider = Staking; + + type RewardDestinationChecker = (); type UnixTime = Timestamp; type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index c0fd4fcc9937..ec03c2a5029b 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -41,7 +41,7 @@ use sp_runtime::{ use sp_staking::{ currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, StakeBalanceProvider, StakeBalanceType, + EraIndex, Page, SessionIndex, Stake, StakingBalanceProvider, StakeBalanceType, StakingAccount::{self, Controller, Stash}, StakingInterface, }; @@ -1823,7 +1823,7 @@ impl StakingInterface for Pallet { } } -impl StakeBalanceProvider for Pallet { +impl StakingBalanceProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 508218dcfd4f..fa26f412c1a1 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,7 +37,7 @@ use sp_runtime::{ }; use sp_staking::{ - EraIndex, Page, SessionIndex, StakeBalanceProvider, + EraIndex, Page, SessionIndex, StakingBalanceProvider, StakingAccount::{self, Controller, Stash}, }; use sp_std::prelude::*; @@ -106,11 +106,13 @@ pub mod pallet { /// Something that provides stakeable balance of an account as well as a way to hold and /// release this stake. - type StakeBalanceProvider: StakeBalanceProvider< + type StakeBalanceProvider: StakingBalanceProvider< Balance = Self::CurrencyBalance, AccountId = Self::AccountId, >; + type RewardDestinationChecker: sp_staking::RewardDestinationChecker; + /// Time used for computing era duration. /// /// It is guaranteed to start being called from the first `on_finalize`. Thus value at @@ -855,6 +857,8 @@ pub mod pallet { BoundNotMet, /// Used when attempting to use deprecated controller account logic. ControllerDeprecated, + /// Provided reward destination is not allowed. + RewardDestinationRestricted, } #[pallet::hooks] diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 6b1cd1632943..6a3ab7374a7d 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -98,9 +98,6 @@ pub struct Stake { #[impl_trait_for_tuples::impl_for_tuples(10)] pub trait OnStakingUpdate { /// Fired when the stake amount of someone updates. - /// - /// This is effectively any changes to the bond amount, such as bonding more funds, and - /// unbonding. fn on_stake_update(_who: &AccountId, _prev_stake: Option>) {} /// Fired when someone sets their intention to nominate. @@ -116,9 +113,6 @@ pub trait OnStakingUpdate { fn on_nominator_update(_who: &AccountId, _prev_nominations: Vec) {} /// Fired when someone removes their intention to nominate, either due to chill or validating. - /// - /// The set of nominations at the time of removal is provided as it can no longer be fetched in - /// any way. fn on_nominator_remove(_who: &AccountId, _nominations: Vec) {} /// Fired when someone sets their intention to validate. @@ -154,9 +148,6 @@ pub trait OnStakingUpdate { } /// A generic representation of a staking implementation. -/// -/// This interface uses the terminology of NPoS, but it is aims to be generic enough to cover other -/// implementations as well. pub trait StakingInterface { /// Balance type used by the staking system. type Balance: Sub @@ -352,10 +343,9 @@ impl< Vec::with_capacity(chunk.len()); for individual in chunk.iter() { page_total.saturating_accrue(individual.value); - others.push(IndividualExposure { - who: individual.who.clone(), - value: individual.value, - }) + others.push( + IndividualExposure { who: individual.who.clone(), value: individual.value } + ) } exposure_pages.push(ExposurePage { page_total, others }); @@ -421,7 +411,7 @@ pub struct PagedExposureMetadata { } /// Something that provides stakeable balance and a mechanism to reserve this balance. -pub trait StakeBalanceProvider { +pub trait StakingBalanceProvider { /// Balance type used by the staking system. type Balance: Sub + Ord @@ -436,14 +426,33 @@ pub trait StakeBalanceProvider { /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; + /// Balance of who which is available for stake. fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; + + /// Update amount held for bonded stake. fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + + /// Release all amount held for stake. fn release(who: &Self::AccountId); #[cfg(feature = "std")] fn stake_type(who: &Self::AccountId) -> StakeBalanceType; } +/// Something that ensures destination for staking rewards is allowed. +pub trait RewardDestinationChecker { + + /// Returns true if `who` is not allowed to have provided `reward_destination`. + fn restrict(who: &AccountId, reward_destination: Option) -> bool; +} + +impl RewardDestinationChecker for () { + fn restrict(_who: &AccountId, _reward_destination: Option) -> bool { + // never restrict + false + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum StakeBalanceType { Direct, From 8cf31b9ad5fbedf140ce69321f1bf3eccce86faf Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 16:40:26 +0100 Subject: [PATCH 028/202] refactor reward acc restricter in delegation support trait --- substrate/frame/delegated-staking/src/lib.rs | 6 ++--- substrate/frame/delegated-staking/src/mock.rs | 2 +- .../frame/delegated-staking/src/tests.rs | 2 +- substrate/frame/staking/src/ledger.rs | 11 ++++----- substrate/frame/staking/src/mock.rs | 5 +--- substrate/frame/staking/src/pallet/impls.rs | 6 ++--- substrate/frame/staking/src/pallet/mod.rs | 12 ++++------ substrate/primitives/staking/src/lib.rs | 23 ++++++------------- 8 files changed, 26 insertions(+), 41 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index c52f93448f5a..513c3a7e11f7 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -47,7 +47,7 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { use super::*; - use sp_staking::StakingBalanceProvider; + use sp_staking::StakingDelegationSupport; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -68,7 +68,7 @@ pub mod pallet { /// Core Staking Balance Provider. /// /// Fallback implementation when an account is not a delegatee. - type FallbackBalanceProvider: StakingBalanceProvider< + type FallbackBalanceProvider: StakingDelegationSupport< Balance = BalanceOf, AccountId = Self::AccountId, >; @@ -409,7 +409,7 @@ impl Delegator for Pallet { } } -impl sp_staking::StakingBalanceProvider for Pallet { +impl sp_staking::StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index e136ee56fa3a..0a586c89b25e 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -108,7 +108,7 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; - type StakeBalanceProvider = DelegatedStaking; + type DelegationSupport = DelegatedStaking; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 0713261b5394..945a2f8a7542 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -21,7 +21,7 @@ use super::*; use crate::{mock::*, Event}; use frame_support::{assert_noop, assert_ok}; use pallet_staking::Error as StakingError; -use sp_staking::{StakingBalanceProvider, StakeBalanceType}; +use sp_staking::{StakingDelegationSupport, StakeBalanceType}; use frame_support::traits::fungible::InspectHold; #[test] diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index a9d46b098aa2..c5cd29276f85 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -18,11 +18,10 @@ //! A Ledger implementation for stakers. use frame_support::defensive; -use sp_staking::{StakingAccount, StakingBalanceProvider}; +use sp_staking::{StakingAccount, StakingDelegationSupport}; use sp_std::prelude::*; use crate::{BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger}; -use sp_staking::RewardDestinationChecker; #[cfg(any(feature = "runtime-benchmarks", test))] use sp_runtime::traits::Zero; @@ -142,7 +141,7 @@ impl StakingLedger { return Err(Error::::NotStash) } - T::StakeBalanceProvider::update_hold(&self.stash, self.total) + T::DelegationSupport::update_hold(&self.stash, self.total) .map_err(|_| Error::::BadState)?; Ledger::::insert( &self.controller().ok_or_else(|| { @@ -163,7 +162,7 @@ impl StakingLedger { return Err(Error::::AlreadyBonded); } - if T::RewardDestinationChecker::restrict(&self.stash, payee.clone().from(&self.stash)) { + if T::DelegationSupport::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { return Err(Error::::RewardDestinationRestricted); } @@ -178,7 +177,7 @@ impl StakingLedger { return Err(Error::::NotStash); } - if T::RewardDestinationChecker::restrict(&self.stash, payee.clone().from(&self.stash)) { + if T::DelegationSupport::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { return Err(Error::::RewardDestinationRestricted); } @@ -192,7 +191,7 @@ impl StakingLedger { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { - T::StakeBalanceProvider::release(&ledger.stash); + T::DelegationSupport::release(&ledger.stash); Ledger::::remove(controller); >::remove(&stash); diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index eb7f9cb9e60f..20c533f62b1b 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -294,10 +294,7 @@ impl OnStakingUpdate for EventListenerMock { impl crate::pallet::pallet::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; - - type StakeBalanceProvider = Staking; - - type RewardDestinationChecker = (); + type DelegationSupport = Staking; type UnixTime = Timestamp; type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index ec03c2a5029b..cd672fd8daf5 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -41,7 +41,7 @@ use sp_runtime::{ use sp_staking::{ currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, StakingBalanceProvider, StakeBalanceType, + EraIndex, Page, SessionIndex, Stake, StakingDelegationSupport, StakeBalanceType, StakingAccount::{self, Controller, Stash}, StakingInterface, }; @@ -1818,12 +1818,12 @@ impl StakingInterface for Pallet { } fn force_unlock(who: &Self::AccountId) -> sp_runtime::DispatchResult { - T::StakeBalanceProvider::release(who); + T::DelegationSupport::release(who); Ok(()) } } -impl StakingBalanceProvider for Pallet { +impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index fa26f412c1a1..a8610ae0a59a 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,7 +37,7 @@ use sp_runtime::{ }; use sp_staking::{ - EraIndex, Page, SessionIndex, StakingBalanceProvider, + EraIndex, Page, SessionIndex, StakingDelegationSupport, StakingAccount::{self, Controller, Stash}, }; use sp_std::prelude::*; @@ -106,13 +106,11 @@ pub mod pallet { /// Something that provides stakeable balance of an account as well as a way to hold and /// release this stake. - type StakeBalanceProvider: StakingBalanceProvider< + type DelegationSupport: StakingDelegationSupport< Balance = Self::CurrencyBalance, AccountId = Self::AccountId, >; - type RewardDestinationChecker: sp_staking::RewardDestinationChecker; - /// Time used for computing era duration. /// /// It is guaranteed to start being called from the first `on_finalize`. Thus value at @@ -713,7 +711,7 @@ pub mod pallet { status ); assert!( - T::StakeBalanceProvider::stakeable_balance(stash) >= balance, + T::DelegationSupport::stakeable_balance(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::bond( @@ -948,7 +946,7 @@ pub mod pallet { frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); + let stash_balance = T::DelegationSupport::stakeable_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::new(stash.clone(), value); @@ -984,7 +982,7 @@ pub mod pallet { let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; - let stash_balance = T::StakeBalanceProvider::stakeable_balance(&stash); + let stash_balance = T::DelegationSupport::stakeable_balance(&stash); if let Some(extra) = stash_balance.checked_sub(&ledger.total) { let extra = extra.min(max_additional); ledger.total += extra; diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 6a3ab7374a7d..3afefc50cef3 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -410,8 +410,8 @@ pub struct PagedExposureMetadata { pub page_count: Page, } -/// Something that provides stakeable balance and a mechanism to reserve this balance. -pub trait StakingBalanceProvider { +/// Something that provides delegation support to core staking. +pub trait StakingDelegationSupport{ /// Balance type used by the staking system. type Balance: Sub + Ord @@ -435,22 +435,13 @@ pub trait StakingBalanceProvider { /// Release all amount held for stake. fn release(who: &Self::AccountId); - #[cfg(feature = "std")] - fn stake_type(who: &Self::AccountId) -> StakeBalanceType; -} - -/// Something that ensures destination for staking rewards is allowed. -pub trait RewardDestinationChecker { - - /// Returns true if `who` is not allowed to have provided `reward_destination`. - fn restrict(who: &AccountId, reward_destination: Option) -> bool; -} - -impl RewardDestinationChecker for () { - fn restrict(_who: &AccountId, _reward_destination: Option) -> bool { - // never restrict + fn restrict_reward_destination(_who: &Self::AccountId, _reward_destination: Option) -> bool { + // never restrict by default false } + + #[cfg(feature = "std")] + fn stake_type(who: &Self::AccountId) -> StakeBalanceType; } #[derive(Clone, Debug, Eq, PartialEq)] From 7d7ce787c3084eda9ba39ff6469998217c0d3213 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 16:42:44 +0100 Subject: [PATCH 029/202] fmt --- substrate/frame/delegated-staking/src/tests.rs | 12 +++++++----- substrate/frame/staking/src/ledger.rs | 10 ++++++++-- substrate/frame/staking/src/lib.rs | 1 - substrate/frame/staking/src/pallet/impls.rs | 4 ++-- substrate/frame/staking/src/pallet/mod.rs | 3 ++- substrate/primitives/staking/src/lib.rs | 14 +++++++++----- 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 945a2f8a7542..6a1193ff4944 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -19,10 +19,9 @@ use super::*; use crate::{mock::*, Event}; -use frame_support::{assert_noop, assert_ok}; +use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use pallet_staking::Error as StakingError; -use sp_staking::{StakingDelegationSupport, StakeBalanceType}; -use frame_support::traits::fungible::InspectHold; +use sp_staking::{StakeBalanceType, StakingDelegationSupport}; #[test] fn create_a_delegatee_with_first_delegator() { @@ -140,10 +139,13 @@ mod integration { // set some delegations for delegator in 200..250 { assert_ok!(DelegatedStaking::delegate(&fund(delegator, 1000), &delegatee, 100)); - assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100); + assert_eq!( + Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), + 100 + ); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); - assert_ok!(Staking::bond(RuntimeOrigin::signed(delegatee), )); + assert_ok!(Staking::bond(RuntimeOrigin::signed(delegatee),)); } // diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index c5cd29276f85..9dac51e42577 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -162,7 +162,10 @@ impl StakingLedger { return Err(Error::::AlreadyBonded); } - if T::DelegationSupport::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { + if T::DelegationSupport::restrict_reward_destination( + &self.stash, + payee.clone().from(&self.stash), + ) { return Err(Error::::RewardDestinationRestricted); } @@ -177,7 +180,10 @@ impl StakingLedger { return Err(Error::::NotStash); } - if T::DelegationSupport::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { + if T::DelegationSupport::restrict_reward_destination( + &self.stash, + payee.clone().from(&self.stash), + ) { return Err(Error::::RewardDestinationRestricted); } diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 97398e457cc0..dc89944d8e01 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -426,7 +426,6 @@ impl RewardDestination { }, } } - } /// Preference of what happens regarding validation. diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index cd672fd8daf5..245e3636d9a6 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -41,9 +41,9 @@ use sp_runtime::{ use sp_staking::{ currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, StakingDelegationSupport, StakeBalanceType, + EraIndex, Page, SessionIndex, Stake, StakeBalanceType, StakingAccount::{self, Controller, Stash}, - StakingInterface, + StakingDelegationSupport, StakingInterface, }; use sp_std::prelude::*; diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index a8610ae0a59a..d20322dbb881 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,8 +37,9 @@ use sp_runtime::{ }; use sp_staking::{ - EraIndex, Page, SessionIndex, StakingDelegationSupport, + EraIndex, Page, SessionIndex, StakingAccount::{self, Controller, Stash}, + StakingDelegationSupport, }; use sp_std::prelude::*; diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 3afefc50cef3..66a4fe299a93 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -343,9 +343,10 @@ impl< Vec::with_capacity(chunk.len()); for individual in chunk.iter() { page_total.saturating_accrue(individual.value); - others.push( - IndividualExposure { who: individual.who.clone(), value: individual.value } - ) + others.push(IndividualExposure { + who: individual.who.clone(), + value: individual.value, + }) } exposure_pages.push(ExposurePage { page_total, others }); @@ -411,7 +412,7 @@ pub struct PagedExposureMetadata { } /// Something that provides delegation support to core staking. -pub trait StakingDelegationSupport{ +pub trait StakingDelegationSupport { /// Balance type used by the staking system. type Balance: Sub + Ord @@ -435,7 +436,10 @@ pub trait StakingDelegationSupport{ /// Release all amount held for stake. fn release(who: &Self::AccountId); - fn restrict_reward_destination(_who: &Self::AccountId, _reward_destination: Option) -> bool { + fn restrict_reward_destination( + _who: &Self::AccountId, + _reward_destination: Option, + ) -> bool { // never restrict by default false } From 59f285d7772387d306091c7fd0e32e346b00a117 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 16:56:16 +0100 Subject: [PATCH 030/202] explicitly set Staking to have NoDelegation impl --- substrate/frame/delegated-staking/src/lib.rs | 16 ++++++++++++++-- substrate/frame/staking/src/lib.rs | 2 +- substrate/frame/staking/src/mock.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 4 +++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 513c3a7e11f7..01fadc6d943c 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -37,7 +37,7 @@ use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{Delegatee, Delegator}, - StakeBalanceType, StakerStatus, StakingInterface, + StakeBalanceType, StakerStatus, StakingDelegationSupport, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -409,7 +409,7 @@ impl Delegator for Pallet { } } -impl sp_staking::StakingDelegationSupport for Pallet { +impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -428,6 +428,18 @@ impl sp_staking::StakingDelegationSupport for Pallet { T::FallbackBalanceProvider::release(who) } + fn restrict_reward_destination( + who: &Self::AccountId, + reward_destination: Option, + ) -> bool { + // for non delegatee accounts, use default implementation. + if !Self::is_delegatee(who) { + return T::FallbackBalanceProvider::restrict_reward_destination(who, reward_destination); + } + + // restrict if reward destination not set or set as delegatee account itself. + reward_destination.map_or_else(|| true, |reward_acc| who == reward_destination) + } #[cfg(feature = "std")] fn stake_type(who: &Self::AccountId) -> StakeBalanceType { if Self::is_delegatee(who) { diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index dc89944d8e01..d8d20fc65105 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -322,7 +322,7 @@ pub use sp_staking::{Exposure, IndividualExposure, StakerStatus}; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; pub use weights::WeightInfo; -pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap}; +pub use pallet::{pallet::*, NoDelegation, UseNominatorsAndValidatorsMap, UseValidatorsMap}; pub(crate) const STAKING_ID: LockIdentifier = *b"staking "; pub(crate) const LOG_TARGET: &str = "runtime::staking"; diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 20c533f62b1b..dfbf5ea53d40 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -294,7 +294,7 @@ impl OnStakingUpdate for EventListenerMock { impl crate::pallet::pallet::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; - type DelegationSupport = Staking; + type DelegationSupport = pallet_staking::NoDelegation; type UnixTime = Timestamp; type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 245e3636d9a6..0a4a49f02864 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1823,7 +1823,8 @@ impl StakingInterface for Pallet { } } -impl StakingDelegationSupport for Pallet { +pub struct NoDelegation(PhantomData); +impl StakingDelegationSupport for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -1845,6 +1846,7 @@ impl StakingDelegationSupport for Pallet { StakeBalanceType::Direct } } + #[cfg(any(test, feature = "try-runtime"))] impl Pallet { pub(crate) fn do_try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { From 1441013a0b1e1a647fa2e0c7c7e5a690883abc51 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 17:14:36 +0100 Subject: [PATCH 031/202] fix delegated staking and refactor --- substrate/frame/delegated-staking/src/lib.rs | 16 ++++++++-------- substrate/frame/delegated-staking/src/mock.rs | 2 +- substrate/frame/delegated-staking/src/tests.rs | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 01fadc6d943c..712d52d7df72 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -65,10 +65,10 @@ pub mod pallet { /// Core staking implementation. type Staking: StakingInterface, AccountId = Self::AccountId>; - /// Core Staking Balance Provider. + /// Non Delegatee Staking Support. /// /// Fallback implementation when an account is not a delegatee. - type FallbackBalanceProvider: StakingDelegationSupport< + type FallbackSupportProvider: StakingDelegationSupport< Balance = BalanceOf, AccountId = Self::AccountId, >; @@ -415,17 +415,17 @@ impl StakingDelegationSupport for Pallet { fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { >::get(who).map_or_else( - || T::FallbackBalanceProvider::stakeable_balance(who), + || T::FallbackSupportProvider::stakeable_balance(who), |delegatee| delegatee.effective_balance(), ) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - T::FallbackBalanceProvider::update_hold(who, amount) + T::FallbackSupportProvider::update_hold(who, amount) } fn release(who: &Self::AccountId) { - T::FallbackBalanceProvider::release(who) + T::FallbackSupportProvider::release(who) } fn restrict_reward_destination( @@ -434,11 +434,11 @@ impl StakingDelegationSupport for Pallet { ) -> bool { // for non delegatee accounts, use default implementation. if !Self::is_delegatee(who) { - return T::FallbackBalanceProvider::restrict_reward_destination(who, reward_destination); + return T::FallbackSupportProvider::restrict_reward_destination(who, reward_destination); } // restrict if reward destination not set or set as delegatee account itself. - reward_destination.map_or_else(|| true, |reward_acc| who == reward_destination) + reward_destination.map_or_else(|| true, |reward_acc| who == &reward_acc) } #[cfg(feature = "std")] fn stake_type(who: &Self::AccountId) -> StakeBalanceType { @@ -446,7 +446,7 @@ impl StakingDelegationSupport for Pallet { return StakeBalanceType::Delegated; } - T::FallbackBalanceProvider::stake_type(who) + T::FallbackSupportProvider::stake_type(who) } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 0a586c89b25e..cab9bf33de3c 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -141,7 +141,7 @@ impl delegated_staking::Config for Runtime { type Currency = Balances; type RuntimeHoldReason = RuntimeHoldReason; type Staking = Staking; - type FallbackBalanceProvider = Staking; + type FallbackSupportProvider = pallet_staking::NoDelegation; } frame_support::construct_runtime!( diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 6a1193ff4944..1b1f63999f6a 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -143,9 +143,9 @@ mod integration { Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100 ); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); + // assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); - assert_ok!(Staking::bond(RuntimeOrigin::signed(delegatee),)); + // assert_ok!(Staking::bond(RuntimeOrigin::signed(delegatee),)); } // From 2a7376d6d6ad79f8abd7bbc1d1749a8ccea58d8d Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 19:43:19 +0100 Subject: [PATCH 032/202] bond test --- substrate/frame/delegated-staking/src/lib.rs | 92 +++++++++++++++---- substrate/frame/delegated-staking/src/mock.rs | 9 +- .../frame/delegated-staking/src/tests.rs | 41 +++++++-- .../primitives/staking/src/delegation.rs | 5 +- 4 files changed, 112 insertions(+), 35 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 712d52d7df72..b7b23f2d3c58 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -26,11 +26,13 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; + use frame_support::{ pallet_prelude::*, traits::{ fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, tokens::{Fortitude, Precision, Preservation}, + DefensiveOption, }, }; use pallet::*; @@ -68,7 +70,7 @@ pub mod pallet { /// Non Delegatee Staking Support. /// /// Fallback implementation when an account is not a delegatee. - type FallbackSupportProvider: StakingDelegationSupport< + type FallbackSupport: StakingDelegationSupport< Balance = BalanceOf, AccountId = Self::AccountId, >; @@ -95,6 +97,8 @@ pub mod pallet { NotDelegatee, /// Some corruption in internal state. BadState, + /// Unapplied pending slash restricts operation on delegatee. + UnappliedSlash, } /// A reason for placing a hold on funds. @@ -147,7 +151,10 @@ pub struct DelegationRegister { pub payee: T::AccountId, /// Sum of all delegated funds to this delegatee. #[codec(compact)] - pub balance: BalanceOf, + pub total_delegated: BalanceOf, + /// Amount that is bonded and held. + #[codec(compact)] + pub hold: BalanceOf, /// Slashes that are not yet applied. #[codec(compact)] pub pending_slash: BalanceOf, @@ -156,8 +163,20 @@ pub struct DelegationRegister { } impl DelegationRegister { - pub fn effective_balance(&self) -> BalanceOf { - self.balance.saturating_sub(self.pending_slash) + /// balance that can be staked. + pub fn delegated_balance(&self) -> BalanceOf { + // do not allow to stake more than unapplied slash + self.total_delegated.saturating_sub(self.pending_slash) + } + + /// balance that is delegated but not bonded. + pub fn unbonded_balance(&self) -> BalanceOf { + self.total_delegated.saturating_sub(self.hold) + } + + /// consumes self and returns Delegation Register with updated hold amount. + pub fn update_hold(self, amount: BalanceOf) -> Self { + DelegationRegister { hold: amount, ..self } } } @@ -165,9 +184,14 @@ impl Delegatee for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn delegate_balance(who: Self::AccountId) -> Self::Balance { + fn delegated_balance(who: &Self::AccountId) -> Self::Balance { >::get(who) - .map_or_else(|| 0u32.into(), |register| register.effective_balance()) + .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) + } + + fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { + >::get(who) + .map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) } fn accept_delegations( @@ -191,7 +215,8 @@ impl Delegatee for Pallet { } else { *maybe_register = Some(DelegationRegister { payee: reward_destination.clone(), - balance: Zero::zero(), + total_delegated: Zero::zero(), + hold: Zero::zero(), pending_slash: Zero::zero(), blocked: false, }); @@ -264,7 +289,7 @@ impl Delegatee for Pallet { fn update_bond(who: &Self::AccountId) -> DispatchResult { let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; - let delegated_balance = delegatee.effective_balance(); + let delegated_balance = delegatee.delegated_balance(); match T::Staking::stake(who) { Ok(stake) => { @@ -303,7 +328,7 @@ impl Delegatee for Pallet { >::mutate(delegatee, |maybe_register| match maybe_register { Some(ledger) => { - ledger.balance.saturating_reduce(value); + ledger.total_delegated.saturating_reduce(value); Ok(()) }, None => { @@ -391,7 +416,7 @@ impl Delegator for Pallet { >::insert(delegator, (delegatee, new_delegation_amount)); >::mutate(delegatee, |maybe_register| { if let Some(register) = maybe_register { - register.balance.saturating_accrue(value); + register.total_delegated.saturating_accrue(value); } }); @@ -415,30 +440,57 @@ impl StakingDelegationSupport for Pallet { fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { >::get(who).map_or_else( - || T::FallbackSupportProvider::stakeable_balance(who), - |delegatee| delegatee.effective_balance(), + || T::FallbackSupport::stakeable_balance(who), + |delegatee| delegatee.delegated_balance(), ) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - T::FallbackSupportProvider::update_hold(who, amount) + if !Self::is_delegatee(who) { + return T::FallbackSupport::update_hold(who, amount); + } + + // delegation register should exist since `who` is a delegatee. + let delegation_register = + >::get(who).defensive_ok_or(Error::::BadState)?; + ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); + ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); + + let updated_register = delegation_register.update_hold(amount); + >::insert(who, updated_register); + + Ok(()) } fn release(who: &Self::AccountId) { - T::FallbackSupportProvider::release(who) + let delegation_register = >::get(who); + if delegation_register.is_some() { + todo!("handle kill delegatee") + } else { + T::FallbackSupport::release(who) + } } fn restrict_reward_destination( who: &Self::AccountId, reward_destination: Option, ) -> bool { - // for non delegatee accounts, use default implementation. - if !Self::is_delegatee(who) { - return T::FallbackSupportProvider::restrict_reward_destination(who, reward_destination); + let maybe_register = >::get(who); + // if not delegatee, use fallback. + if maybe_register.is_none() { + return T::FallbackSupport::restrict_reward_destination(who, reward_destination); } - // restrict if reward destination not set or set as delegatee account itself. - reward_destination.map_or_else(|| true, |reward_acc| who == &reward_acc) + // restrict if reward destination is not set + if reward_destination.is_none() { + return true; + } + + let register = maybe_register.expect("checked above; qed"); + let reward_acc = reward_destination.expect("checked above; qed"); + + // restrict if reward account is not what delegatee registered. + register.payee != reward_acc } #[cfg(feature = "std")] fn stake_type(who: &Self::AccountId) -> StakeBalanceType { @@ -446,7 +498,7 @@ impl StakingDelegationSupport for Pallet { return StakeBalanceType::Delegated; } - T::FallbackSupportProvider::stake_type(who) + T::FallbackSupport::stake_type(who) } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index cab9bf33de3c..cc80708761ea 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -17,16 +17,13 @@ use crate::{self as delegated_staking}; use frame_support::{ - assert_ok, derive_impl, + derive_impl, pallet_prelude::*, parameter_types, traits::{ConstU64, Currency}, }; -use sp_runtime::{ - traits::{Convert, IdentityLookup}, - BuildStorage, Perbill, -}; +use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill}; use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, @@ -141,7 +138,7 @@ impl delegated_staking::Config for Runtime { type Currency = Balances; type RuntimeHoldReason = RuntimeHoldReason; type Staking = Staking; - type FallbackSupportProvider = pallet_staking::NoDelegation; + type FallbackSupport = pallet_staking::NoDelegation; } frame_support::construct_runtime!( diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 1b1f63999f6a..d2937f082af6 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -18,7 +18,7 @@ //! Tests for pallet-delegated-staking. use super::*; -use crate::{mock::*, Event}; +use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use pallet_staking::Error as StakingError; use sp_staking::{StakeBalanceType, StakingDelegationSupport}; @@ -124,6 +124,7 @@ fn migrate_to_delegator() { /// Integration tests with pallet-staking. mod integration { use super::*; + use pallet_staking::RewardDestination; #[test] fn bond() { @@ -131,24 +132,48 @@ mod integration { let delegatee: AccountId = 99; let reward_acc: AccountId = 100; assert_eq!(Staking::status(&delegatee), Err(StakingError::::NotStash.into())); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); - assert_eq!(Balances::free_balance(delegatee), 0); // set intention to become a delegatee assert_ok!(DelegatedStaking::accept_delegations(&fund(delegatee, 100), &reward_acc)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); + + // first delegation + assert_ok!(DelegatedStaking::delegate(&fund(200, 200), &delegatee, 100)); + // stakeable balance is now 100. + let mut expected_stakeable_balance = 100; + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), expected_stakeable_balance); + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); + // bond delegatee + assert_ok!(Staking::bond( + RuntimeOrigin::signed(delegatee), + 100, + RewardDestination::Account(reward_acc) + )); + // after bond, unbonded balance is 0 + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + // set some delegations - for delegator in 200..250 { - assert_ok!(DelegatedStaking::delegate(&fund(delegator, 1000), &delegatee, 100)); + for delegator in 201..250 { + assert_ok!(DelegatedStaking::delegate(&fund(delegator, 200), &delegatee, 100)); + expected_stakeable_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100 ); - // assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); - // assert_ok!(Staking::bond(RuntimeOrigin::signed(delegatee),)); + assert_eq!( + DelegatedStaking::stakeable_balance(&delegatee), + expected_stakeable_balance + ); + + // unbonded balance is the newly delegated 100 + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), 100)); + // after bond, unbonded balance is 0 + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); } - // + // check ledger total stake.. }); } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 71e756e47cfd..64a27d607094 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -37,7 +37,10 @@ pub trait Delegatee { type AccountId: Clone + sp_std::fmt::Debug; /// Total delegated balance to this account. - fn delegate_balance(who: Self::AccountId) -> Self::Balance; + fn delegated_balance(who: &Self::AccountId) -> Self::Balance; + + /// Total delegated balance to this account that is not yet bonded to staking. + fn unbonded_balance(who: &Self::AccountId) -> Self::Balance; /// Set intention to accept delegations. fn accept_delegations( From 8b2d6417e97344211df3740b90256c28777451a3 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 19:56:03 +0100 Subject: [PATCH 033/202] check stake --- substrate/frame/delegated-staking/src/tests.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index d2937f082af6..cca9d42204e4 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -125,6 +125,7 @@ fn migrate_to_delegator() { mod integration { use super::*; use pallet_staking::RewardDestination; + use sp_staking::Stake; #[test] fn bond() { @@ -173,15 +174,13 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); } - // check ledger total stake.. + assert_eq!(Staking::stake(&delegatee).unwrap(), Stake { + total: 50*100, + active: 50*100, + }) }); } - #[test] - fn bond_extra() { - ExtBuilder::default().build_and_execute(|| assert!(true)); - } - #[test] fn partial_withdraw() { ExtBuilder::default().build_and_execute(|| assert!(true)); From e072a83596bfcbb4f736b9005b11ead2e158b5e1 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 20:37:14 +0100 Subject: [PATCH 034/202] test refactor --- substrate/frame/delegated-staking/src/mock.rs | 41 +++++++++++++++---- .../frame/delegated-staking/src/tests.rs | 22 ++++++---- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index cc80708761ea..ac8d3ada170c 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -16,12 +16,7 @@ // limitations under the License. use crate::{self as delegated_staking}; -use frame_support::{ - derive_impl, - pallet_prelude::*, - parameter_types, - traits::{ConstU64, Currency}, -}; +use frame_support::{assert_ok, derive_impl, pallet_prelude::*, parameter_types, traits::{ConstU64, Currency}}; use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill}; @@ -29,6 +24,12 @@ use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, }; +use frame_support::traits::fungible::InspectHold; +use sp_staking::delegation::Delegatee; +use sp_staking::StakingDelegationSupport; +use crate::pallet::HoldReason; +use pallet_staking::RewardDestination; +use sp_staking::delegation::Delegator; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -228,7 +229,31 @@ impl ExtBuilder { } /// fund and return who. -pub fn fund(who: AccountId, amount: Balance) -> AccountId { - let _ = Balances::deposit_creating(&who, amount); +pub fn fund(who: &AccountId, amount: Balance) -> &AccountId { + let _ = Balances::deposit_creating(who, amount); who } + + +pub fn setup_delegation(delegatee: AccountId, reward_acc: AccountId, delegators: Vec, delegate_amount: Balance) { + assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); + assert_ok!(DelegatedStaking::delegate(fund(&delegators[0], delegate_amount + ExistentialDeposit::get()), &delegatee, delegate_amount)); + assert_ok!(Staking::bond( + RuntimeOrigin::signed(delegatee), + delegate_amount, + RewardDestination::Account(reward_acc) + )); + + for delegator in &delegators[1..] { + assert_ok!(DelegatedStaking::delegate(fund(delegator, 200), &delegatee, 100)); + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), 100)); + } + + + // sanity checks + assert_eq!( + DelegatedStaking::stakeable_balance(&delegatee), + delegate_amount * delegators.len() as Balance + ); + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); +} \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index cca9d42204e4..516dda307881 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -31,10 +31,10 @@ fn create_a_delegatee_with_first_delegator() { let delegator: AccountId = 202; // set intention to accept delegation. - assert_ok!(DelegatedStaking::accept_delegations(&fund(delegatee, 1000), &reward_account)); + assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 1000), &reward_account)); // delegate to this account - assert_ok!(DelegatedStaking::delegate(&fund(delegator, 1000), &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(fund(&delegator, 1000), &delegatee, 100)); // verify assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); @@ -77,7 +77,7 @@ fn create_multiple_delegators() { let reward_account: AccountId = 201; // before becoming a delegatee, stakeable balance is only direct balance. - assert_eq!(DelegatedStaking::stake_type(&fund(delegatee, 1000)), StakeBalanceType::Direct); + assert_eq!(DelegatedStaking::stake_type(fund(&delegatee, 1000)), StakeBalanceType::Direct); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 1000); // set intention to accept delegation. @@ -86,7 +86,7 @@ fn create_multiple_delegators() { // create 100 delegators for i in 202..302 { assert_ok!(DelegatedStaking::delegate( - &fund(i, 100 + ExistentialDeposit::get()), + fund(&i, 100 + ExistentialDeposit::get()), &delegatee, 100 )); @@ -135,11 +135,11 @@ mod integration { assert_eq!(Staking::status(&delegatee), Err(StakingError::::NotStash.into())); // set intention to become a delegatee - assert_ok!(DelegatedStaking::accept_delegations(&fund(delegatee, 100), &reward_acc)); + assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); // first delegation - assert_ok!(DelegatedStaking::delegate(&fund(200, 200), &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(fund(&200, 200), &delegatee, 100)); // stakeable balance is now 100. let mut expected_stakeable_balance = 100; assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), expected_stakeable_balance); @@ -155,7 +155,7 @@ mod integration { // set some delegations for delegator in 201..250 { - assert_ok!(DelegatedStaking::delegate(&fund(delegator, 200), &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(fund(&delegator, 200), &delegatee, 100)); expected_stakeable_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), @@ -183,7 +183,13 @@ mod integration { #[test] fn partial_withdraw() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + let delegatee: AccountId = 99; + let reward_acc: AccountId = 100; + let delegators: Vec = (200..250).collect(); + let delegate_amount: Balance = 100; + setup_delegation(delegatee, reward_acc, delegators, delegate_amount) + }); } #[test] From 5b881f72aa642e95cacad1fcde8316788d530720 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 20:39:44 +0100 Subject: [PATCH 035/202] test fix --- substrate/frame/delegated-staking/src/mock.rs | 45 ++++++++++++------- .../frame/delegated-staking/src/tests.rs | 12 ++--- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index ac8d3ada170c..28241d0b0ff8 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -16,7 +16,12 @@ // limitations under the License. use crate::{self as delegated_staking}; -use frame_support::{assert_ok, derive_impl, pallet_prelude::*, parameter_types, traits::{ConstU64, Currency}}; +use frame_support::{ + assert_ok, derive_impl, + pallet_prelude::*, + parameter_types, + traits::{ConstU64, Currency}, +}; use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill}; @@ -24,12 +29,11 @@ use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, }; -use frame_support::traits::fungible::InspectHold; -use sp_staking::delegation::Delegatee; -use sp_staking::StakingDelegationSupport; -use crate::pallet::HoldReason; use pallet_staking::RewardDestination; -use sp_staking::delegation::Delegator; +use sp_staking::{ + delegation::{Delegatee, Delegator}, + StakingDelegationSupport, +}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -234,26 +238,33 @@ pub fn fund(who: &AccountId, amount: Balance) -> &AccountId { who } - -pub fn setup_delegation(delegatee: AccountId, reward_acc: AccountId, delegators: Vec, delegate_amount: Balance) { +pub fn setup_delegation( + delegatee: AccountId, + reward_acc: AccountId, + delegators: Vec, + delegate_amount: Balance, +) { assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); - assert_ok!(DelegatedStaking::delegate(fund(&delegators[0], delegate_amount + ExistentialDeposit::get()), &delegatee, delegate_amount)); + assert_ok!(DelegatedStaking::delegate( + fund(&delegators[0], delegate_amount + ExistentialDeposit::get()), + &delegatee, + delegate_amount + )); assert_ok!(Staking::bond( - RuntimeOrigin::signed(delegatee), - delegate_amount, - RewardDestination::Account(reward_acc) - )); + RuntimeOrigin::signed(delegatee), + delegate_amount, + RewardDestination::Account(reward_acc) + )); for delegator in &delegators[1..] { - assert_ok!(DelegatedStaking::delegate(fund(delegator, 200), &delegatee, 100)); - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), 100)); + assert_ok!(DelegatedStaking::delegate(fund(delegator, delegate_amount + ExistentialDeposit::get()), &delegatee, delegate_amount)); + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), delegate_amount)); } - // sanity checks assert_eq!( DelegatedStaking::stakeable_balance(&delegatee), delegate_amount * delegators.len() as Balance ); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); -} \ No newline at end of file +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 516dda307881..3c20c500e147 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -174,10 +174,10 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); } - assert_eq!(Staking::stake(&delegatee).unwrap(), Stake { - total: 50*100, - active: 50*100, - }) + assert_eq!( + Staking::stake(&delegatee).unwrap(), + Stake { total: 50 * 100, active: 50 * 100 } + ) }); } @@ -186,8 +186,8 @@ mod integration { ExtBuilder::default().build_and_execute(|| { let delegatee: AccountId = 99; let reward_acc: AccountId = 100; - let delegators: Vec = (200..250).collect(); - let delegate_amount: Balance = 100; + let delegators: Vec = (200..300).collect(); + let delegate_amount: Balance = 200; setup_delegation(delegatee, reward_acc, delegators, delegate_amount) }); } From 171c4d9ba544aa16e02785863a652c48ca90f72a Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 3 Dec 2023 21:01:57 +0100 Subject: [PATCH 036/202] new fn in Staking Interface partial_withdraw_unbond --- substrate/frame/delegated-staking/src/tests.rs | 13 ++++++++----- substrate/frame/staking/src/pallet/impls.rs | 14 +++++++++++++- substrate/frame/staking/src/pallet/mod.rs | 4 ++-- substrate/primitives/staking/src/lib.rs | 9 +++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 3c20c500e147..6aa233252827 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -184,11 +184,14 @@ mod integration { #[test] fn partial_withdraw() { ExtBuilder::default().build_and_execute(|| { - let delegatee: AccountId = 99; - let reward_acc: AccountId = 100; - let delegators: Vec = (200..300).collect(); - let delegate_amount: Balance = 200; - setup_delegation(delegatee, reward_acc, delegators, delegate_amount) + let delegatee: AccountId = 200; + let reward_acc: AccountId = 201; + let delegators: Vec = (300..400).collect(); + let delegate_amount: Balance = 500; + setup_delegation(delegatee, reward_acc, delegators, delegate_amount); + + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 100)); + }); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 0a4a49f02864..535bf0946fc6 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -121,11 +121,12 @@ impl Pallet { pub(super) fn do_withdraw_unbonded( controller: &T::AccountId, num_slashing_spans: u32, + maybe_limit: Option>, ) -> Result { let mut ledger = Self::ledger(Controller(controller.clone()))?; let (stash, old_total) = (ledger.stash.clone(), ledger.total); if let Some(current_era) = Self::current_era() { - ledger = ledger.consolidate_unlocked(current_era, None) + ledger = ledger.consolidate_unlocked(current_era, maybe_limit) } let new_total = ledger.total; @@ -1713,6 +1714,7 @@ impl StakingInterface for Pallet { Self::chill(RawOrigin::Signed(ctrl).into()) } + // FIXME(ank4n): Refactor withdraw unbonded to take limit and remove partial_withdraw_unbonded fn withdraw_unbonded( who: Self::AccountId, num_slashing_spans: u32, @@ -1723,6 +1725,16 @@ impl StakingInterface for Pallet { .map_err(|with_post| with_post.error) } + fn partial_withdraw_unbonded( + who: Self::AccountId, + num_slashing_spans: u32, + maybe_limit: Option>, + ) -> Result { + let ctrl = Self::bonded(&who).ok_or(Error::::NotStash)?; + Self::do_withdraw_unbonded(&ctrl, num_slashing_spans, maybe_limit) + .map(|_| !Ledger::::contains_key(&ctrl)) + } + fn bond( who: &Self::AccountId, value: Self::Balance, diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index d20322dbb881..d6dabff9f61c 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -1043,7 +1043,7 @@ pub mod pallet { if unlocking == T::MaxUnlockingChunks::get() as usize { let real_num_slashing_spans = Self::slashing_spans(&controller).map_or(0, |s| s.iter().count()); - Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32)?) + Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32, None)?) } else { None } @@ -1147,7 +1147,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let controller = ensure_signed(origin)?; - let actual_weight = Self::do_withdraw_unbonded(&controller, num_slashing_spans)?; + let actual_weight = Self::do_withdraw_unbonded(&controller, num_slashing_spans, None)?; Ok(Some(actual_weight).into()) } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 66a4fe299a93..9b40d26e12d5 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -248,6 +248,15 @@ pub trait StakingInterface { num_slashing_spans: u32, ) -> Result; + /// Unlock any funds schedule to unlock before or at the current era upto a provided limit. + /// + /// Returns whether the stash was killed because of this withdraw or not. + fn partial_withdraw_unbonded( + stash: Self::AccountId, + num_slashing_spans: u32, + maybe_limit: Option, + ) -> Result; + /// The ideal number of active validators. fn desired_validator_count() -> u32; From cc4aae2bb2ec52b4f812362a150692a7e69497ff Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 4 Dec 2023 19:59:27 +0100 Subject: [PATCH 037/202] move to delegation module --- substrate/frame/delegated-staking/src/lib.rs | 6 +-- substrate/frame/delegated-staking/src/mock.rs | 11 ++--- .../frame/delegated-staking/src/tests.rs | 3 +- substrate/frame/staking/src/ledger.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 5 ++- substrate/frame/staking/src/pallet/mod.rs | 8 +++- .../primitives/staking/src/delegation.rs | 43 +++++++++++++++++++ substrate/primitives/staking/src/lib.rs | 43 ------------------- 8 files changed, 63 insertions(+), 58 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index b7b23f2d3c58..03a317d6f6a8 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -38,8 +38,8 @@ use frame_support::{ use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ - delegation::{Delegatee, Delegator}, - StakeBalanceType, StakerStatus, StakingDelegationSupport, StakingInterface, + delegation::{Delegatee, Delegator, StakeBalanceType, StakingDelegationSupport}, + StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -49,7 +49,7 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { use super::*; - use sp_staking::StakingDelegationSupport; + use sp_staking::delegation::StakingDelegationSupport; #[pallet::pallet] pub struct Pallet(PhantomData); diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 28241d0b0ff8..f995ed8f797c 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -30,10 +30,7 @@ use frame_election_provider_support::{ onchain, SequentialPhragmen, }; use pallet_staking::RewardDestination; -use sp_staking::{ - delegation::{Delegatee, Delegator}, - StakingDelegationSupport, -}; +use sp_staking::delegation::{Delegatee, Delegator, StakingDelegationSupport}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -257,7 +254,11 @@ pub fn setup_delegation( )); for delegator in &delegators[1..] { - assert_ok!(DelegatedStaking::delegate(fund(delegator, delegate_amount + ExistentialDeposit::get()), &delegatee, delegate_amount)); + assert_ok!(DelegatedStaking::delegate( + fund(delegator, delegate_amount + ExistentialDeposit::get()), + &delegatee, + delegate_amount + )); assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), delegate_amount)); } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 6aa233252827..27040d1f3933 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -21,7 +21,7 @@ use super::*; use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use pallet_staking::Error as StakingError; -use sp_staking::{StakeBalanceType, StakingDelegationSupport}; +use sp_staking::delegation::{StakeBalanceType, StakingDelegationSupport}; #[test] fn create_a_delegatee_with_first_delegator() { @@ -191,7 +191,6 @@ mod integration { setup_delegation(delegatee, reward_acc, delegators, delegate_amount); assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 100)); - }); } diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 9dac51e42577..b636eedf4e57 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -18,7 +18,7 @@ //! A Ledger implementation for stakers. use frame_support::defensive; -use sp_staking::{StakingAccount, StakingDelegationSupport}; +use sp_staking::{delegation::StakingDelegationSupport, StakingAccount}; use sp_std::prelude::*; use crate::{BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger}; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 535bf0946fc6..d0738c599a8b 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -40,10 +40,11 @@ use sp_runtime::{ }; use sp_staking::{ currency_to_vote::CurrencyToVote, + delegation::{StakeBalanceType, StakingDelegationSupport}, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, StakeBalanceType, + EraIndex, Page, SessionIndex, Stake, StakingAccount::{self, Controller, Stash}, - StakingDelegationSupport, StakingInterface, + StakingInterface, }; use sp_std::prelude::*; diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index d6dabff9f61c..a2fed2a640e5 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,9 +37,9 @@ use sp_runtime::{ }; use sp_staking::{ + delegation::StakingDelegationSupport, EraIndex, Page, SessionIndex, StakingAccount::{self, Controller, Stash}, - StakingDelegationSupport, }; use sp_std::prelude::*; @@ -1043,7 +1043,11 @@ pub mod pallet { if unlocking == T::MaxUnlockingChunks::get() as usize { let real_num_slashing_spans = Self::slashing_spans(&controller).map_or(0, |s| s.iter().count()); - Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32, None)?) + Some(Self::do_withdraw_unbonded( + &controller, + real_num_slashing_spans as u32, + None, + )?) } else { None } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 64a27d607094..bb1db7aa8038 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -140,3 +140,46 @@ pub trait Delegator { value: Self::Balance, ) -> DispatchResult; } + +/// Something that provides delegation support to core staking. +pub trait StakingDelegationSupport { + /// Balance type used by the staking system. + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + + /// Balance of who which is available for stake. + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; + + /// Update amount held for bonded stake. + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + + /// Release all amount held for stake. + fn release(who: &Self::AccountId); + + fn restrict_reward_destination( + _who: &Self::AccountId, + _reward_destination: Option, + ) -> bool { + // never restrict by default + false + } + + #[cfg(feature = "std")] + fn stake_type(who: &Self::AccountId) -> StakeBalanceType; +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum StakeBalanceType { + Direct, + Delegated, +} diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 9b40d26e12d5..6bf0204fc4d8 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -420,47 +420,4 @@ pub struct PagedExposureMetadata { pub page_count: Page, } -/// Something that provides delegation support to core staking. -pub trait StakingDelegationSupport { - /// Balance type used by the staking system. - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - - /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; - - /// Balance of who which is available for stake. - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; - - /// Update amount held for bonded stake. - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; - - /// Release all amount held for stake. - fn release(who: &Self::AccountId); - - fn restrict_reward_destination( - _who: &Self::AccountId, - _reward_destination: Option, - ) -> bool { - // never restrict by default - false - } - - #[cfg(feature = "std")] - fn stake_type(who: &Self::AccountId) -> StakeBalanceType; -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum StakeBalanceType { - Direct, - Delegated, -} - sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From b8bed53ae40c42782bf5b46f28b5f5f5e488afc7 Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 4 Dec 2023 20:59:30 +0100 Subject: [PATCH 038/202] compiles after refactor --- substrate/frame/delegated-staking/src/lib.rs | 66 +++++++++---------- substrate/frame/delegated-staking/src/mock.rs | 3 +- substrate/frame/staking/src/ledger.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 33 ++++++---- .../primitives/staking/src/delegation.rs | 28 ++------ substrate/primitives/staking/src/lib.rs | 22 +++++++ 6 files changed, 83 insertions(+), 71 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 03a317d6f6a8..54caace122af 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -65,15 +65,8 @@ pub mod pallet { type RuntimeHoldReason: From; /// Core staking implementation. - type Staking: StakingInterface, AccountId = Self::AccountId>; - - /// Non Delegatee Staking Support. - /// - /// Fallback implementation when an account is not a delegatee. - type FallbackSupport: StakingDelegationSupport< - Balance = BalanceOf, - AccountId = Self::AccountId, - >; + type CoreStaking: StakingInterface, AccountId = Self::AccountId> + + sp_staking::StakingHoldProvider, AccountId = Self::AccountId>; } #[pallet::error] @@ -202,7 +195,7 @@ impl Delegatee for Pallet { ensure!(!>::contains_key(who), Error::::NotAllowed); // make sure they are not already a direct staker - ensure!(T::Staking::status(who).is_err(), Error::::AlreadyStaker); + ensure!(T::CoreStaking::status(who).is_err(), Error::::AlreadyStaker); // payee account cannot be same as delegatee ensure!(reward_destination != who, Error::::InvalidRewardDestination); @@ -246,16 +239,16 @@ impl Delegatee for Pallet { ); // ensure staker is a nominator - let status = T::Staking::status(new_delegatee)?; + let status = T::CoreStaking::status(new_delegatee)?; match status { StakerStatus::Nominator(_) => (), _ => return Err(Error::::InvalidDelegation.into()), } - let stake = T::Staking::stake(new_delegatee)?; + let stake = T::CoreStaking::stake(new_delegatee)?; // unlock funds from staker - T::Staking::force_unlock(new_delegatee)?; + T::CoreStaking::force_unlock(new_delegatee)?; // try transferring the staked amount. This should never fail but if it does, it indicates // bad state and we abort. @@ -291,14 +284,14 @@ impl Delegatee for Pallet { let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; let delegated_balance = delegatee.delegated_balance(); - match T::Staking::stake(who) { + match T::CoreStaking::stake(who) { Ok(stake) => { let unstaked_delegated_balance = delegated_balance.saturating_sub(stake.total); - T::Staking::bond_extra(who, unstaked_delegated_balance) + T::CoreStaking::bond_extra(who, unstaked_delegated_balance) }, Err(_) => { // If stake not found, it means this is the first bond - T::Staking::bond(who, delegated_balance, &delegatee.payee) + T::CoreStaking::bond(who, delegated_balance, &delegatee.payee) }, } } @@ -434,20 +427,13 @@ impl Delegator for Pallet { } } -impl StakingDelegationSupport for Pallet { +impl sp_staking::StakingHoldProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who).map_or_else( - || T::FallbackSupport::stakeable_balance(who), - |delegatee| delegatee.delegated_balance(), - ) - } - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { if !Self::is_delegatee(who) { - return T::FallbackSupport::update_hold(who, amount); + return T::CoreStaking::update_hold(who, amount); } // delegation register should exist since `who` is a delegatee. @@ -463,12 +449,20 @@ impl StakingDelegationSupport for Pallet { } fn release(who: &Self::AccountId) { - let delegation_register = >::get(who); - if delegation_register.is_some() { - todo!("handle kill delegatee") - } else { - T::FallbackSupport::release(who) + if !Self::is_delegatee(who) { + T::CoreStaking::release(who); } + + let _delegation_register = >::get(who); + todo!("handle kill delegatee") + } +} +impl StakingDelegationSupport for Pallet { + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + >::get(who).map_or_else( + || T::Currency::reducible_balance(who, Preservation::Expendable, Fortitude::Polite), + |delegatee| delegatee.delegated_balance(), + ) } fn restrict_reward_destination( @@ -476,9 +470,10 @@ impl StakingDelegationSupport for Pallet { reward_destination: Option, ) -> bool { let maybe_register = >::get(who); - // if not delegatee, use fallback. + if maybe_register.is_none() { - return T::FallbackSupport::restrict_reward_destination(who, reward_destination); + // no restrictions for non delegatees. + return false; } // restrict if reward destination is not set @@ -492,13 +487,14 @@ impl StakingDelegationSupport for Pallet { // restrict if reward account is not what delegatee registered. register.payee != reward_acc } + #[cfg(feature = "std")] fn stake_type(who: &Self::AccountId) -> StakeBalanceType { - if Self::is_delegatee(who) { - return StakeBalanceType::Delegated; + if !Self::is_delegatee(who) { + return StakeBalanceType::Direct; } - T::FallbackSupport::stake_type(who) + StakeBalanceType::Delegated } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index f995ed8f797c..52551881f89d 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -139,8 +139,7 @@ impl delegated_staking::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type RuntimeHoldReason = RuntimeHoldReason; - type Staking = Staking; - type FallbackSupport = pallet_staking::NoDelegation; + type CoreStaking = Staking; } frame_support::construct_runtime!( diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index b636eedf4e57..9a4f2ade3f11 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -18,7 +18,7 @@ //! A Ledger implementation for stakers. use frame_support::defensive; -use sp_staking::{delegation::StakingDelegationSupport, StakingAccount}; +use sp_staking::{delegation::StakingDelegationSupport, StakingAccount, StakingHoldProvider}; use sp_std::prelude::*; use crate::{BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger}; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index d0738c599a8b..204ad17978b8 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -40,11 +40,11 @@ use sp_runtime::{ }; use sp_staking::{ currency_to_vote::CurrencyToVote, - delegation::{StakeBalanceType, StakingDelegationSupport}, + delegation::StakingDelegationSupport, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, Page, SessionIndex, Stake, StakingAccount::{self, Controller, Stash}, - StakingInterface, + StakingHoldProvider, StakingInterface, }; use sp_std::prelude::*; @@ -1836,15 +1836,10 @@ impl StakingInterface for Pallet { } } -pub struct NoDelegation(PhantomData); -impl StakingDelegationSupport for NoDelegation { +impl StakingHoldProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::free_balance(who) - } - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); Ok(()) @@ -1853,10 +1848,26 @@ impl StakingDelegationSupport for NoDelegation { fn release(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } +} + +/// Standard implementation of `StakingDelegationSupport` that supports only direct staking and no +/// delegated staking. +pub struct NoDelegation(PhantomData); +impl StakingHoldProvider for NoDelegation { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { + Pallet::::update_hold(who, amount) + } - #[cfg(feature = "std")] - fn stake_type(_: &Self::AccountId) -> StakeBalanceType { - StakeBalanceType::Direct + fn release(who: &Self::AccountId) { + Pallet::::release(who) + } +} +impl StakingDelegationSupport for NoDelegation { + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::free_balance(who) } } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index bb1db7aa8038..acff7a87e2a5 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::StakingHoldProvider; use codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{DispatchResult, Saturating}; @@ -142,30 +143,11 @@ pub trait Delegator { } /// Something that provides delegation support to core staking. -pub trait StakingDelegationSupport { - /// Balance type used by the staking system. - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - - /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; - +pub trait StakingDelegationSupport: StakingHoldProvider { /// Balance of who which is available for stake. fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; - /// Update amount held for bonded stake. - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; - - /// Release all amount held for stake. - fn release(who: &Self::AccountId); - + /// Returns true if provided reward destination is not allowed. fn restrict_reward_destination( _who: &Self::AccountId, _reward_destination: Option, @@ -175,7 +157,9 @@ pub trait StakingDelegationSupport { } #[cfg(feature = "std")] - fn stake_type(who: &Self::AccountId) -> StakeBalanceType; + fn stake_type(_who: &Self::AccountId) -> StakeBalanceType { + StakeBalanceType::Direct + } } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 6bf0204fc4d8..13656b55c01c 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -420,4 +420,26 @@ pub struct PagedExposureMetadata { pub page_count: Page, } +/// Something that can hold and release funds for staking. +pub trait StakingHoldProvider { + /// Balance type used by the staking system. + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + + /// Update amount held for bonded stake. + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + + /// Release all amount held for stake. + fn release(who: &Self::AccountId); +} sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From 51cfa262b9e9a677d380be4e5415ff2268426d71 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 16:53:39 +0100 Subject: [PATCH 039/202] incomplete withdraw test --- substrate/frame/delegated-staking/src/mock.rs | 16 ++++++-- .../frame/delegated-staking/src/tests.rs | 38 ++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 52551881f89d..1b203fb4d041 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -29,7 +29,7 @@ use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, }; -use pallet_staking::RewardDestination; +use pallet_staking::{RewardDestination, CurrentEra}; use sp_staking::delegation::{Delegatee, Delegator, StakingDelegationSupport}; pub type T = Runtime; @@ -91,7 +91,6 @@ pallet_staking_reward_curve::build! { parameter_types! { pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS; pub static BondingDuration: u32 = 3; - pub static CurrentEra: u32 = 0; pub static ElectionsBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build(); } pub struct OnChainSeqPhragmen; @@ -229,12 +228,12 @@ impl ExtBuilder { } /// fund and return who. -pub fn fund(who: &AccountId, amount: Balance) -> &AccountId { +pub(crate) fn fund(who: &AccountId, amount: Balance) -> &AccountId { let _ = Balances::deposit_creating(who, amount); who } -pub fn setup_delegation( +pub(crate) fn setup_delegation( delegatee: AccountId, reward_acc: AccountId, delegators: Vec, @@ -268,3 +267,12 @@ pub fn setup_delegation( ); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); } + +pub(crate) fn start_era(era: sp_staking::EraIndex) { + CurrentEra::::set(Some(era)); +} + +pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool { + use sp_staking::{StakingInterface, Stake}; + Staking::stake(&who).unwrap() == Stake { total, active } +} \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 27040d1f3933..e92990b6fdf4 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -182,15 +182,49 @@ mod integration { } #[test] - fn partial_withdraw() { + fn withdraw_test() { ExtBuilder::default().build_and_execute(|| { + // initial era + start_era(1); let delegatee: AccountId = 200; let reward_acc: AccountId = 201; let delegators: Vec = (300..400).collect(); let delegate_amount: Balance = 500; - setup_delegation(delegatee, reward_acc, delegators, delegate_amount); + setup_delegation(delegatee, reward_acc, delegators.clone(), delegate_amount); + let expected_staked = delegate_amount * delegators.len() as Balance; + // lets go to a new era + start_era(2); + + assert!(eq_stake(delegatee, expected_staked, expected_staked)); + // Withdrawing without unbonding would not do anything assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 100)); + // active and total stake remains same + assert!(eq_stake(delegatee, expected_staked, expected_staked)); + + // 200 wants to unbond 50 in era 2, withdrawable in era 5. + assert_ok!(Staking::unbond(RuntimeOrigin::signed(delegatee), 50)); + // 201 wants to unbond 100 in era 3, withdrawable in era 6. + start_era(3); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(delegatee), 100)); + // 202 wants to unbond 100 in era 4, withdrawable in era 7. + start_era(4); + assert_ok!(Staking::unbond(RuntimeOrigin::signed(delegatee), 200)); + + // active stake is now reduced.. + let mut expected_active = expected_staked - (50 + 100 + 200); + assert!(eq_stake(delegatee, expected_staked, expected_active)); + + // lets try withdrawing now, still should do nothing as we are at era 4. + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 50)); + assert!(eq_stake(delegatee, expected_staked, expected_active)); + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + // full amount is still delegated + assert_eq!(DelegatedStaking::delegated_balance(&delegatee), expected_staked); + + start_era(5); + // at era 5, 50 tokens are withdrawable + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 50)); }); } From 4a5659c3b0ef66e611446897a8d0fbe688071277 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 18:00:57 +0100 Subject: [PATCH 040/202] introduce withdraw_exact, test failing --- substrate/frame/delegated-staking/src/lib.rs | 110 ++++++++++-------- substrate/frame/delegated-staking/src/mock.rs | 15 +-- .../frame/delegated-staking/src/tests.rs | 43 +++---- substrate/frame/staking/src/pallet/impls.rs | 19 +-- substrate/frame/staking/src/pallet/mod.rs | 4 +- .../primitives/staking/src/delegation.rs | 9 +- substrate/primitives/staking/src/lib.rs | 6 +- 7 files changed, 97 insertions(+), 109 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 54caace122af..cda7a4d2aba7 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -88,6 +88,8 @@ pub mod pallet { NotEnoughFunds, /// Not an existing delegatee account. NotDelegatee, + /// Not a Delegator account. + NotDelegator, /// Some corruption in internal state. BadState, /// Unapplied pending slash restricts operation on delegatee. @@ -282,16 +284,15 @@ impl Delegatee for Pallet { fn update_bond(who: &Self::AccountId) -> DispatchResult { let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; - let delegated_balance = delegatee.delegated_balance(); + let amount_to_bond = delegatee.unbonded_balance(); match T::CoreStaking::stake(who) { Ok(stake) => { - let unstaked_delegated_balance = delegated_balance.saturating_sub(stake.total); - T::CoreStaking::bond_extra(who, unstaked_delegated_balance) + T::CoreStaking::bond_extra(who, amount_to_bond) }, Err(_) => { // If stake not found, it means this is the first bond - T::CoreStaking::bond(who, delegated_balance, &delegatee.payee) + T::CoreStaking::bond(who, amount_to_bond, &delegatee.payee) }, } } @@ -300,46 +301,11 @@ impl Delegatee for Pallet { delegator: &Self::AccountId, delegatee: &Self::AccountId, value: Self::Balance, + num_slashing_spans: u32, ) -> DispatchResult { - >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { - Some((current_delegatee, delegate_balance)) => { - ensure!(¤t_delegatee.clone() == delegatee, Error::::NotDelegatee); - ensure!(*delegate_balance >= value, Error::::NotAllowed); - - delegate_balance.saturating_reduce(value); - - if *delegate_balance == BalanceOf::::zero() { - *maybe_delegate = None; - } - Ok(()) - }, - None => { - // delegator does not exist - return Err(Error::::NotAllowed) - }, - })?; - - >::mutate(delegatee, |maybe_register| match maybe_register { - Some(ledger) => { - ledger.total_delegated.saturating_reduce(value); - Ok(()) - }, - None => { - // Delegatee not found - return Err(Error::::NotDelegatee) - }, - })?; - - let released = T::Currency::release( - &HoldReason::Delegating.into(), - &delegator, - value, - Precision::BestEffort, - )?; - - defensive_assert!(released == value, "hold should have been released fully"); - - Ok(()) + // fixme(ank4n) handle killing of stash + let _ = T::CoreStaking::withdraw_exact(delegatee, value, num_slashing_spans); + Self::delegation_withdraw(delegator, delegatee, value) } fn apply_slash( @@ -365,7 +331,7 @@ impl Delegatee for Pallet { ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); // remove delegation of `value` from `existing_delegator`. - Self::withdraw(existing_delegator, delegatee, value)?; + Self::delegation_withdraw(existing_delegator, delegatee, value)?; // transfer the withdrawn value to `new_delegator`. T::Currency::transfer(existing_delegator, new_delegator, value, Preservation::Expendable) @@ -418,12 +384,8 @@ impl Delegator for Pallet { Ok(()) } - fn request_undelegate( - delegator: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult { - todo!() + fn unbond(delegatee: &Self::AccountId, value: Self::Balance) -> DispatchResult { + T::CoreStaking::unbond(delegatee, value) } } @@ -502,6 +464,52 @@ impl Pallet { fn is_delegatee(who: &T::AccountId) -> bool { >::contains_key(who) } + + fn delegation_withdraw( + delegator: &T::AccountId, + delegatee: &T::AccountId, + value: BalanceOf, + ) -> DispatchResult { + >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { + Some((current_delegatee, delegate_balance)) => { + ensure!(¤t_delegatee.clone() == delegatee, Error::::NotDelegatee); + ensure!(*delegate_balance >= value, Error::::NotEnoughFunds); + + delegate_balance.saturating_reduce(value); + + if *delegate_balance == BalanceOf::::zero() { + *maybe_delegate = None; + } + Ok(()) + }, + None => { + // delegator does not exist + return Err(Error::::NotDelegator) + }, + })?; + + >::mutate(delegatee, |maybe_register| match maybe_register { + Some(ledger) => { + ledger.total_delegated.saturating_reduce(value); + Ok(()) + }, + None => { + // Delegatee not found + return Err(Error::::NotDelegatee) + }, + })?; + + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &delegator, + value, + Precision::BestEffort, + )?; + + defensive_assert!(released == value, "hold should have been released fully"); + + Ok(()) + } } #[cfg(any(test, feature = "try-runtime"))] @@ -509,4 +517,4 @@ impl Pallet { pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { Ok(()) } -} +} \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 1b203fb4d041..149310ef9627 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -240,24 +240,13 @@ pub(crate) fn setup_delegation( delegate_amount: Balance, ) { assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); - assert_ok!(DelegatedStaking::delegate( - fund(&delegators[0], delegate_amount + ExistentialDeposit::get()), - &delegatee, - delegate_amount - )); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(delegatee), - delegate_amount, - RewardDestination::Account(reward_acc) - )); - - for delegator in &delegators[1..] { + for delegator in &delegators { assert_ok!(DelegatedStaking::delegate( fund(delegator, delegate_amount + ExistentialDeposit::get()), &delegatee, delegate_amount )); - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), delegate_amount)); + assert_ok!(DelegatedStaking::update_bond(&delegatee)); } // sanity checks diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index e92990b6fdf4..83eb4952219e 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -138,25 +138,11 @@ mod integration { assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); - // first delegation - assert_ok!(DelegatedStaking::delegate(fund(&200, 200), &delegatee, 100)); - // stakeable balance is now 100. - let mut expected_stakeable_balance = 100; - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), expected_stakeable_balance); - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); - // bond delegatee - assert_ok!(Staking::bond( - RuntimeOrigin::signed(delegatee), - 100, - RewardDestination::Account(reward_acc) - )); - // after bond, unbonded balance is 0 - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); - + let mut delegated_balance: Balance = 0; // set some delegations - for delegator in 201..250 { + for delegator in 200..250 { assert_ok!(DelegatedStaking::delegate(fund(&delegator, 200), &delegatee, 100)); - expected_stakeable_balance += 100; + delegated_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100 @@ -164,12 +150,12 @@ mod integration { assert_eq!( DelegatedStaking::stakeable_balance(&delegatee), - expected_stakeable_balance + delegated_balance ); // unbonded balance is the newly delegated 100 assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); - assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(delegatee), 100)); + assert_ok!(DelegatedStaking::update_bond(&delegatee)); // after bond, unbonded balance is 0 assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); } @@ -198,25 +184,26 @@ mod integration { assert!(eq_stake(delegatee, expected_staked, expected_staked)); // Withdrawing without unbonding would not do anything - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 100)); + assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 50, 0)); + // assert_noop!(DelegatedStaking::withdraw(&200, &delegatee, 50, 0), Error::::NotAllowed); // active and total stake remains same assert!(eq_stake(delegatee, expected_staked, expected_staked)); - // 200 wants to unbond 50 in era 2, withdrawable in era 5. - assert_ok!(Staking::unbond(RuntimeOrigin::signed(delegatee), 50)); - // 201 wants to unbond 100 in era 3, withdrawable in era 6. + // 300 wants to unbond 50 in era 2, withdrawable in era 5. + assert_ok!(DelegatedStaking::unbond(&delegatee, 50)); + // 301 wants to unbond 100 in era 3, withdrawable in era 6. start_era(3); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(delegatee), 100)); - // 202 wants to unbond 100 in era 4, withdrawable in era 7. + assert_ok!(DelegatedStaking::unbond(&delegatee, 100)); + // 302 wants to unbond 100 in era 4, withdrawable in era 7. start_era(4); - assert_ok!(Staking::unbond(RuntimeOrigin::signed(delegatee), 200)); + assert_ok!(DelegatedStaking::unbond(&delegatee, 200)); // active stake is now reduced.. let mut expected_active = expected_staked - (50 + 100 + 200); assert!(eq_stake(delegatee, expected_staked, expected_active)); // lets try withdrawing now, still should do nothing as we are at era 4. - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 50)); + assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 50, 0)); assert!(eq_stake(delegatee, expected_staked, expected_active)); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); // full amount is still delegated @@ -224,7 +211,7 @@ mod integration { start_era(5); // at era 5, 50 tokens are withdrawable - assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(delegatee), 50)); + assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 50, 0)); }); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 204ad17978b8..e0fbd903f596 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -122,15 +122,20 @@ impl Pallet { pub(super) fn do_withdraw_unbonded( controller: &T::AccountId, num_slashing_spans: u32, - maybe_limit: Option>, + maybe_amount: Option>, ) -> Result { let mut ledger = Self::ledger(Controller(controller.clone()))?; let (stash, old_total) = (ledger.stash.clone(), ledger.total); if let Some(current_era) = Self::current_era() { - ledger = ledger.consolidate_unlocked(current_era, maybe_limit) + ledger = ledger.consolidate_unlocked(current_era, maybe_amount) } let new_total = ledger.total; + if let Some(amount) = maybe_amount { + ensure!( + old_total.saturating_sub(new_total) == amount, + Error::::NotEnoughFunds); + }; let used_weight = if ledger.unlocking.is_empty() && ledger.active < T::Currency::minimum_balance() { // This account must have called `unbond()` with some value that caused the active @@ -1726,13 +1731,13 @@ impl StakingInterface for Pallet { .map_err(|with_post| with_post.error) } - fn partial_withdraw_unbonded( - who: Self::AccountId, + fn withdraw_exact( + who: &Self::AccountId, + amount: BalanceOf, num_slashing_spans: u32, - maybe_limit: Option>, ) -> Result { - let ctrl = Self::bonded(&who).ok_or(Error::::NotStash)?; - Self::do_withdraw_unbonded(&ctrl, num_slashing_spans, maybe_limit) + let ctrl = Self::bonded(who).ok_or(Error::::NotStash)?; + Self::do_withdraw_unbonded(&ctrl, num_slashing_spans, Some(amount)) .map(|_| !Ledger::::contains_key(&ctrl)) } diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index a2fed2a640e5..13f357e39e80 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -858,6 +858,9 @@ pub mod pallet { ControllerDeprecated, /// Provided reward destination is not allowed. RewardDestinationRestricted, + // FIXME(ank4n): look at the error again + /// Not enough funds available to withdraw + NotEnoughFunds, } #[pallet::hooks] @@ -1058,7 +1061,6 @@ pub mod pallet { let mut ledger = Self::ledger(Controller(controller))?; let mut value = value.min(ledger.active); let stash = ledger.stash.clone(); - ensure!( ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize, Error::::NoMoreChunks, diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index acff7a87e2a5..fb35e2e88b3d 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -84,6 +84,7 @@ pub trait Delegatee { delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, + num_slashing_spans: u32, ) -> DispatchResult; /// Applies a pending slash on delegatee by passing a delegator account who should be slashed @@ -134,12 +135,8 @@ pub trait Delegator { value: Self::Balance, ) -> DispatchResult; - /// Request removal of delegated stake. - fn request_undelegate( - delegator: &Self::AccountId, - delegatee: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult; + /// Unbond stake. + fn unbond(delegatee: &Self::AccountId, value: Self::Balance) -> DispatchResult; } /// Something that provides delegation support to core staking. diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 13656b55c01c..5bbe49fa8f21 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -251,10 +251,10 @@ pub trait StakingInterface { /// Unlock any funds schedule to unlock before or at the current era upto a provided limit. /// /// Returns whether the stash was killed because of this withdraw or not. - fn partial_withdraw_unbonded( - stash: Self::AccountId, + fn withdraw_exact( + stash: &Self::AccountId, + amount: Self::Balance, num_slashing_spans: u32, - maybe_limit: Option, ) -> Result; /// The ideal number of active validators. From 6e4ea51fd20bfc449bdb531556c0a90d416638a6 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 19:51:12 +0100 Subject: [PATCH 041/202] tests for withdraw --- substrate/frame/delegated-staking/src/lib.rs | 21 +++-- substrate/frame/delegated-staking/src/mock.rs | 31 ++++-- .../frame/delegated-staking/src/tests.rs | 94 ++++++++++++++----- .../primitives/staking/src/delegation.rs | 5 +- 4 files changed, 104 insertions(+), 47 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index cda7a4d2aba7..15d7bb07b469 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -27,14 +27,11 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -use frame_support::{ - pallet_prelude::*, - traits::{ - fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, - tokens::{Fortitude, Precision, Preservation}, - DefensiveOption, - }, -}; +use frame_support::{pallet_prelude::*, traits::{ + fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, + tokens::{Fortitude, Precision, Preservation}, + DefensiveOption, +}, transactional}; use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ @@ -94,6 +91,8 @@ pub mod pallet { BadState, /// Unapplied pending slash restricts operation on delegatee. UnappliedSlash, + /// Failed to withdraw amount from Core Staking Ledger. + WithdrawFailed, } /// A reason for placing a hold on funds. @@ -297,6 +296,7 @@ impl Delegatee for Pallet { } } + #[transactional] fn withdraw( delegator: &Self::AccountId, delegatee: &Self::AccountId, @@ -304,7 +304,7 @@ impl Delegatee for Pallet { num_slashing_spans: u32, ) -> DispatchResult { // fixme(ank4n) handle killing of stash - let _ = T::CoreStaking::withdraw_exact(delegatee, value, num_slashing_spans); + let _stash_killed: bool = T::CoreStaking::withdraw_exact(delegatee, value, num_slashing_spans).map_err(|_| Error::::WithdrawFailed)?; Self::delegation_withdraw(delegator, delegatee, value) } @@ -319,6 +319,7 @@ impl Delegatee for Pallet { /// Move funds from proxy delegator to actual delegator. // TODO: Keep track of proxy delegator and only allow movement from proxy -> new delegator + #[transactional] fn migrate_delegator( delegatee: &Self::AccountId, existing_delegator: &Self::AccountId, @@ -401,9 +402,9 @@ impl sp_staking::StakingHoldProvider for Pallet { // delegation register should exist since `who` is a delegatee. let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; + ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = delegation_register.update_hold(amount); >::insert(who, updated_register); diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 149310ef9627..c0d12a7013dc 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -29,7 +29,7 @@ use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, }; -use pallet_staking::{RewardDestination, CurrentEra}; +use pallet_staking::{CurrentEra, RewardDestination}; use sp_staking::delegation::{Delegatee, Delegator, StakingDelegationSupport}; pub type T = Runtime; @@ -233,18 +233,27 @@ pub(crate) fn fund(who: &AccountId, amount: Balance) -> &AccountId { who } -pub(crate) fn setup_delegation( +/// Sets up delegation for passed delegators, returns total delegated amount. +/// +/// `delegate_amount` is incremented by the amount `increment` starting with `base_delegate_amount` +/// from lower index to higher index of delegators. +pub(crate) fn setup_delegation_stake( delegatee: AccountId, reward_acc: AccountId, delegators: Vec, - delegate_amount: Balance, -) { + base_delegate_amount: Balance, + increment: Balance, +) -> Balance { assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); - for delegator in &delegators { + let mut delegated_amount: Balance = 0; + for (index, delegator) in delegators.iter().enumerate() { + let amount_to_delegate = base_delegate_amount + increment * index as Balance; + delegated_amount += amount_to_delegate; + assert_ok!(DelegatedStaking::delegate( - fund(delegator, delegate_amount + ExistentialDeposit::get()), + fund(delegator, amount_to_delegate + ExistentialDeposit::get()), &delegatee, - delegate_amount + amount_to_delegate )); assert_ok!(DelegatedStaking::update_bond(&delegatee)); } @@ -252,9 +261,11 @@ pub(crate) fn setup_delegation( // sanity checks assert_eq!( DelegatedStaking::stakeable_balance(&delegatee), - delegate_amount * delegators.len() as Balance + delegated_amount ); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + + delegated_amount } pub(crate) fn start_era(era: sp_staking::EraIndex) { @@ -262,6 +273,6 @@ pub(crate) fn start_era(era: sp_staking::EraIndex) { } pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool { - use sp_staking::{StakingInterface, Stake}; + use sp_staking::{Stake, StakingInterface}; Staking::stake(&who).unwrap() == Stake { total, active } -} \ No newline at end of file +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 83eb4952219e..19f2b7785fe5 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -148,10 +148,7 @@ mod integration { 100 ); - assert_eq!( - DelegatedStaking::stakeable_balance(&delegatee), - delegated_balance - ); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), delegated_balance); // unbonded balance is the newly delegated 100 assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); @@ -174,44 +171,89 @@ mod integration { start_era(1); let delegatee: AccountId = 200; let reward_acc: AccountId = 201; - let delegators: Vec = (300..400).collect(); - let delegate_amount: Balance = 500; - setup_delegation(delegatee, reward_acc, delegators.clone(), delegate_amount); - let expected_staked = delegate_amount * delegators.len() as Balance; + let delegators: Vec = (301..=350).collect(); + let total_staked = + setup_delegation_stake(delegatee, reward_acc, delegators.clone(), 10, 10); // lets go to a new era start_era(2); - assert!(eq_stake(delegatee, expected_staked, expected_staked)); - // Withdrawing without unbonding would not do anything - assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 50, 0)); - // assert_noop!(DelegatedStaking::withdraw(&200, &delegatee, 50, 0), Error::::NotAllowed); - // active and total stake remains same - assert!(eq_stake(delegatee, expected_staked, expected_staked)); - - // 300 wants to unbond 50 in era 2, withdrawable in era 5. + assert!(eq_stake(delegatee, total_staked, total_staked)); + // Withdrawing without unbonding would fail. + assert_noop!( + DelegatedStaking::withdraw(&300, &delegatee, 50, 0), + Error::::WithdrawFailed + ); + // assert_noop!(DelegatedStaking::withdraw(&200, &delegatee, 50, 0), + // Error::::NotAllowed); active and total stake remains same + assert!(eq_stake(delegatee, total_staked, total_staked)); + + // 305 wants to unbond 50 in era 2, withdrawable in era 5. assert_ok!(DelegatedStaking::unbond(&delegatee, 50)); - // 301 wants to unbond 100 in era 3, withdrawable in era 6. + // 310 wants to unbond 100 in era 3, withdrawable in era 6. start_era(3); assert_ok!(DelegatedStaking::unbond(&delegatee, 100)); - // 302 wants to unbond 100 in era 4, withdrawable in era 7. + // 320 wants to unbond 200 in era 4, withdrawable in era 7. start_era(4); assert_ok!(DelegatedStaking::unbond(&delegatee, 200)); // active stake is now reduced.. - let mut expected_active = expected_staked - (50 + 100 + 200); - assert!(eq_stake(delegatee, expected_staked, expected_active)); + let mut expected_active = total_staked - (50 + 100 + 200); + assert!(eq_stake(delegatee, total_staked, expected_active)); + + // nothing to withdraw at era 4 + assert_noop!( + DelegatedStaking::withdraw(&305, &delegatee, 50, 0), + Error::::WithdrawFailed + ); - // lets try withdrawing now, still should do nothing as we are at era 4. - assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 50, 0)); - assert!(eq_stake(delegatee, expected_staked, expected_active)); + assert!(eq_stake(delegatee, total_staked, expected_active)); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); // full amount is still delegated - assert_eq!(DelegatedStaking::delegated_balance(&delegatee), expected_staked); + assert_eq!(DelegatedStaking::delegated_balance(&delegatee), total_staked); start_era(5); - // at era 5, 50 tokens are withdrawable - assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 50, 0)); + // at era 5, 50 tokens are withdrawable, cannot withdraw more. + assert_noop!( + DelegatedStaking::withdraw(&305, &delegatee, 51, 0), + Error::::WithdrawFailed + ); + // less is possible + assert_ok!(DelegatedStaking::withdraw(&305, &delegatee, 30, 0)); + assert_ok!(DelegatedStaking::withdraw(&305, &delegatee, 20, 0)); + + // Lets go to future era where everything is unbonded. Withdrawable amount: 100 + 200 + start_era(7); + // 305 has no more amount delegated so it cannot withdraw. + assert_noop!( + DelegatedStaking::withdraw(&305, &delegatee, 5, 0), + Error::::NotDelegator + ); + // 309 is an active delegator but has total delegation of 90, so it cannot withdraw more + // than that. + assert_noop!( + DelegatedStaking::withdraw(&309, &delegatee, 91, 0), + Error::::NotEnoughFunds + ); + // 310 cannot withdraw more than delegated funds. + assert_noop!( + DelegatedStaking::withdraw(&310, &delegatee, 101, 0), + Error::::NotEnoughFunds + ); + // but can withdraw all its delegation amount. + assert_ok!(DelegatedStaking::withdraw(&310, &delegatee, 100, 0)); + // 320 can withdraw all its delegation amount. + assert_ok!(DelegatedStaking::withdraw(&320, &delegatee, 200, 0)); + + // cannot withdraw anything more.. + assert_noop!( + DelegatedStaking::withdraw(&301, &delegatee, 1, 0), + Error::::WithdrawFailed + ); + assert_noop!( + DelegatedStaking::withdraw(&350, &delegatee, 1, 0), + Error::::WithdrawFailed + ); }); } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index fb35e2e88b3d..f50b953dc65a 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -79,7 +79,10 @@ pub trait Delegatee { /// Update bond whenever there is a new delegate funds that are not staked. fn update_bond(delegatee: &Self::AccountId) -> DispatchResult; - /// Request removal of delegated stake. + /// Request withdrawal of unbonded stake of `delegatee` belonging to the provided `delegator`. + /// + /// Important: It is upto `delegatee` to enforce which `delegator` can withdraw `value`. The + /// withdrawn value is released in `delegator`'s account. fn withdraw( delegatee: &Self::AccountId, delegator: &Self::AccountId, From 9ce869feceeeff374fc2f6210a985abc490ac0b1 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 19:53:26 +0100 Subject: [PATCH 042/202] rearrange --- substrate/frame/delegated-staking/src/tests.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 19f2b7785fe5..01a90ebc7e72 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -116,11 +116,6 @@ fn distribute_rewards() { ExtBuilder::default().build_and_execute(|| assert!(true)); } -#[test] -fn migrate_to_delegator() { - ExtBuilder::default().build_and_execute(|| assert!(true)); -} - /// Integration tests with pallet-staking. mod integration { use super::*; @@ -266,4 +261,10 @@ mod integration { fn slash_works() { ExtBuilder::default().build_and_execute(|| assert!(true)); } + + #[test] + fn migration_works() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } + } From bba90d186263f6c2e6f19188d60163746ea828dd Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 21:43:57 +0100 Subject: [PATCH 043/202] test to impl --- substrate/frame/delegated-staking/src/tests.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 01a90ebc7e72..0c2cb356459a 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -253,13 +253,23 @@ mod integration { } #[test] - fn claim_reward() { + fn reward_destination_cannot_be_delegatee() { ExtBuilder::default().build_and_execute(|| assert!(true)); } + fn nominate_test() { + ExtBuilder::default().build_and_execute(|| assert!(true)); + } #[test] fn slash_works() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + setup_delegation_stake(200, 201, (210..250).collect(), 100, 0); + start_era(1); + + // delegatee is slashed + + + }); } #[test] From 1b28825369433a33e951fc520e5b9392486facd5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 22:51:29 +0100 Subject: [PATCH 044/202] impl Staking Interface for Delegated Staking --- substrate/frame/delegated-staking/src/lib.rs | 248 ++++++++++++++++-- substrate/frame/delegated-staking/src/mock.rs | 7 +- .../frame/delegated-staking/src/tests.rs | 4 +- .../primitives/staking/src/delegation.rs | 3 - 4 files changed, 226 insertions(+), 36 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 15d7bb07b469..9936639d6b8f 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -27,16 +27,20 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -use frame_support::{pallet_prelude::*, traits::{ - fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, - tokens::{Fortitude, Precision, Preservation}, - DefensiveOption, -}, transactional}; +use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, + tokens::{Fortitude, Precision, Preservation}, + DefensiveOption, + }, + transactional, +}; use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{Delegatee, Delegator, StakeBalanceType, StakingDelegationSupport}, - StakerStatus, StakingInterface, + EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -46,7 +50,6 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { use super::*; - use sp_staking::delegation::StakingDelegationSupport; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -93,6 +96,8 @@ pub mod pallet { UnappliedSlash, /// Failed to withdraw amount from Core Staking Ledger. WithdrawFailed, + /// This operation is not supported with Delegation Staking. + NotSupported, } /// A reason for placing a hold on funds. @@ -119,7 +124,7 @@ pub mod pallet { Withdrawn { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, } - /// Map of Delegators to their delegation. + /// Map of Delegators to their delegation, i.e. (delegatee, delegation_amount). /// /// Note: We are not using a double map with delegator and delegatee account as keys since we /// want to restrict delegators to delegate only to one account. @@ -277,7 +282,7 @@ impl Delegatee for Pallet { }) } - fn kill_delegatee(delegatee: &Self::AccountId) -> DispatchResult { + fn kill_delegatee(_delegatee: &Self::AccountId) -> DispatchResult { todo!() } @@ -286,13 +291,10 @@ impl Delegatee for Pallet { let amount_to_bond = delegatee.unbonded_balance(); match T::CoreStaking::stake(who) { - Ok(stake) => { - T::CoreStaking::bond_extra(who, amount_to_bond) - }, - Err(_) => { - // If stake not found, it means this is the first bond - T::CoreStaking::bond(who, amount_to_bond, &delegatee.payee) - }, + // already bonded + Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), + // first bond + Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegatee.payee), } } @@ -304,15 +306,17 @@ impl Delegatee for Pallet { num_slashing_spans: u32, ) -> DispatchResult { // fixme(ank4n) handle killing of stash - let _stash_killed: bool = T::CoreStaking::withdraw_exact(delegatee, value, num_slashing_spans).map_err(|_| Error::::WithdrawFailed)?; + let _stash_killed: bool = + T::CoreStaking::withdraw_exact(delegatee, value, num_slashing_spans) + .map_err(|_| Error::::WithdrawFailed)?; Self::delegation_withdraw(delegator, delegatee, value) } fn apply_slash( - delegatee: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - reporter: Option, + _delegatee: &Self::AccountId, + _delegator: &Self::AccountId, + _value: Self::Balance, + _reporter: Option, ) -> DispatchResult { todo!() } @@ -384,10 +388,6 @@ impl Delegator for Pallet { Ok(()) } - - fn unbond(delegatee: &Self::AccountId, value: Self::Balance) -> DispatchResult { - T::CoreStaking::unbond(delegatee, value) - } } impl sp_staking::StakingHoldProvider for Pallet { @@ -461,11 +461,207 @@ impl StakingDelegationSupport for Pallet { } } +/// StakingInterface implementation with delegation support. +/// +/// Only supports Nominators via Delegated Bonds. It is possible for a nominator to migrate to a +/// Delegatee. +impl StakingInterface for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + type CurrencyToVote = ::CurrencyToVote; + + fn minimum_nominator_bond() -> Self::Balance { + T::CoreStaking::minimum_nominator_bond() + } + + fn minimum_validator_bond() -> Self::Balance { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::minimum_validator_bond() + } + + fn stash_by_ctrl(_controller: &Self::AccountId) -> Result { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + // ctrl are deprecated, just return err. + Err(Error::::NotSupported.into()) + } + + fn bonding_duration() -> EraIndex { + T::CoreStaking::bonding_duration() + } + + fn current_era() -> EraIndex { + T::CoreStaking::current_era() + } + + fn stake(who: &Self::AccountId) -> Result, DispatchError> { + if Self::is_delegatee(who) { + return T::CoreStaking::stake(who); + } + + Err(Error::::NotSupported.into()) + } + + fn total_stake(who: &Self::AccountId) -> Result { + if Self::is_delegatee(who) { + return T::CoreStaking::total_stake(who); + } + + if Self::is_delegator(who) { + let (_, delegation_amount) = + >::get(who).defensive_ok_or(Error::::BadState)?; + return Ok(delegation_amount) + } + + Err(Error::::NotSupported.into()) + } + + fn active_stake(who: &Self::AccountId) -> Result { + T::CoreStaking::active_stake(who) + } + + fn is_unbonding(who: &Self::AccountId) -> Result { + T::CoreStaking::is_unbonding(who) + } + + fn fully_unbond(who: &Self::AccountId) -> DispatchResult { + if Self::is_delegatee(who) { + return T::CoreStaking::fully_unbond(who); + } + + Err(Error::::NotSupported.into()) + } + + fn bond( + who: &Self::AccountId, + value: Self::Balance, + payee: &Self::AccountId, + ) -> DispatchResult { + // ensure who is not already staked + ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegatee); + let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; + + ensure!(delegation_register.unbonded_balance() >= value, Error::::NotEnoughFunds); + ensure!(delegation_register.payee == *payee, Error::::InvalidRewardDestination); + + T::CoreStaking::bond(who, value, payee) + } + + fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { + if Self::is_delegatee(who) { + return T::CoreStaking::nominate(who, validators); + } + + Err(Error::::NotSupported.into()) + } + + fn chill(who: &Self::AccountId) -> DispatchResult { + if Self::is_delegatee(who) { + return T::CoreStaking::chill(who); + } + + Err(Error::::NotSupported.into()) + } + + fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { + let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; + ensure!(delegation_register.unbonded_balance() >= extra, Error::::NotEnoughFunds); + + T::CoreStaking::bond_extra(who, extra) + } + + fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { + let delegation_register = >::get(stash).ok_or(Error::::NotDelegatee)?; + ensure!(delegation_register.hold >= value, Error::::NotEnoughFunds); + + T::CoreStaking::unbond(stash, value) + } + + /// Not supported, call [`Delegatee::withdraw`] + fn withdraw_unbonded( + _stash: Self::AccountId, + _num_slashing_spans: u32, + ) -> Result { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + /// Not supported, call [`Delegatee::withdraw`] + fn withdraw_exact( + _stash: &Self::AccountId, + _amount: Self::Balance, + _num_slashing_spans: u32, + ) -> Result { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + fn desired_validator_count() -> u32 { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::desired_validator_count() + } + + fn election_ongoing() -> bool { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::election_ongoing() + } + + fn force_unstake(_who: Self::AccountId) -> DispatchResult { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::is_exposed_in_era(who, era) + } + + fn status(who: &Self::AccountId) -> Result, DispatchError> { + ensure!(Self::is_delegatee(who), Error::::NotSupported); + T::CoreStaking::status(who) + } + + fn is_validator(who: &Self::AccountId) -> bool { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::is_validator(who) + } + + fn nominations(who: &Self::AccountId) -> Option> { + T::CoreStaking::nominations(who) + } + + fn force_unlock(_who: &Self::AccountId) -> DispatchResult { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn max_exposure_page_size() -> sp_staking::Page { + T::CoreStaking::max_exposure_page_size() + } + + #[cfg(feature = "runtime-benchmarks")] + fn add_era_stakers( + current_era: &EraIndex, + stash: &Self::AccountId, + exposures: Vec<(Self::AccountId, Self::Balance)>, + ) { + T::CoreStaking::add_era_stakers(current_era, stash, exposures) + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_current_era(era: EraIndex) { + T::CoreStaking::set_current_era(era) + } +} impl Pallet { fn is_delegatee(who: &T::AccountId) -> bool { >::contains_key(who) } + fn is_delegator(who: &T::AccountId) -> bool { + >::contains_key(who) + } + fn delegation_withdraw( delegator: &T::AccountId, delegatee: &T::AccountId, @@ -518,4 +714,4 @@ impl Pallet { pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { Ok(()) } -} \ No newline at end of file +} diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index c0d12a7013dc..dd76c2ef9483 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -29,7 +29,7 @@ use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, }; -use pallet_staking::{CurrentEra, RewardDestination}; +use pallet_staking::CurrentEra; use sp_staking::delegation::{Delegatee, Delegator, StakingDelegationSupport}; pub type T = Runtime; @@ -259,10 +259,7 @@ pub(crate) fn setup_delegation_stake( } // sanity checks - assert_eq!( - DelegatedStaking::stakeable_balance(&delegatee), - delegated_amount - ); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), delegated_amount); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); delegated_amount diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 0c2cb356459a..7812819c7f8a 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -119,7 +119,6 @@ fn distribute_rewards() { /// Integration tests with pallet-staking. mod integration { use super::*; - use pallet_staking::RewardDestination; use sp_staking::Stake; #[test] @@ -193,7 +192,7 @@ mod integration { assert_ok!(DelegatedStaking::unbond(&delegatee, 200)); // active stake is now reduced.. - let mut expected_active = total_staked - (50 + 100 + 200); + let expected_active = total_staked - (50 + 100 + 200); assert!(eq_stake(delegatee, total_staked, expected_active)); // nothing to withdraw at era 4 @@ -257,6 +256,7 @@ mod integration { ExtBuilder::default().build_and_execute(|| assert!(true)); } + #[test] fn nominate_test() { ExtBuilder::default().build_and_execute(|| assert!(true)); } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index f50b953dc65a..a956824df066 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -137,9 +137,6 @@ pub trait Delegator { delegatee: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; - - /// Unbond stake. - fn unbond(delegatee: &Self::AccountId, value: Self::Balance) -> DispatchResult; } /// Something that provides delegation support to core staking. From 8f8b1d6e3e8a57422a298353c690493d0ad8be2a Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 6 Dec 2023 22:53:40 +0100 Subject: [PATCH 045/202] fmt --- substrate/frame/delegated-staking/src/tests.rs | 3 --- substrate/frame/staking/src/pallet/impls.rs | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 7812819c7f8a..c2f7ae8074f9 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -267,8 +267,6 @@ mod integration { start_era(1); // delegatee is slashed - - }); } @@ -276,5 +274,4 @@ mod integration { fn migration_works() { ExtBuilder::default().build_and_execute(|| assert!(true)); } - } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index e0fbd903f596..aa0a11598d95 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -132,9 +132,7 @@ impl Pallet { let new_total = ledger.total; if let Some(amount) = maybe_amount { - ensure!( - old_total.saturating_sub(new_total) == amount, - Error::::NotEnoughFunds); + ensure!(old_total.saturating_sub(new_total) == amount, Error::::NotEnoughFunds); }; let used_weight = if ledger.unlocking.is_empty() && ledger.active < T::Currency::minimum_balance() { From dd21409a2c8722fdc0d9c644d3ec3b22c1df5291 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 7 Dec 2023 00:08:06 +0100 Subject: [PATCH 046/202] merge delegatee and delegator in one --- substrate/frame/delegated-staking/src/lib.rs | 16 +++------- substrate/frame/delegated-staking/src/mock.rs | 2 +- .../frame/delegated-staking/src/tests.rs | 8 ++--- substrate/frame/staking/src/pallet/impls.rs | 2 ++ .../primitives/staking/src/delegation.rs | 30 ++----------------- 5 files changed, 14 insertions(+), 44 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 9936639d6b8f..d4129b103faa 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -39,7 +39,7 @@ use frame_support::{ use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ - delegation::{Delegatee, Delegator, StakeBalanceType, StakingDelegationSupport}, + delegation::{Delegatee, StakingDelegationSupport}, EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -345,11 +345,6 @@ impl Delegatee for Pallet { // add the above removed delegation to `new_delegator`. Self::delegate(new_delegator, delegatee, value) } -} - -impl Delegator for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; fn delegate( delegator: &Self::AccountId, @@ -388,6 +383,7 @@ impl Delegator for Pallet { Ok(()) } + } impl sp_staking::StakingHoldProvider for Pallet { @@ -452,12 +448,8 @@ impl StakingDelegationSupport for Pallet { } #[cfg(feature = "std")] - fn stake_type(who: &Self::AccountId) -> StakeBalanceType { - if !Self::is_delegatee(who) { - return StakeBalanceType::Direct; - } - - StakeBalanceType::Delegated + fn is_delegatee(who: &Self::AccountId) -> bool { + Self::is_delegatee(who) } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index dd76c2ef9483..9e80ee3da371 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -30,7 +30,7 @@ use frame_election_provider_support::{ onchain, SequentialPhragmen, }; use pallet_staking::CurrentEra; -use sp_staking::delegation::{Delegatee, Delegator, StakingDelegationSupport}; +use sp_staking::delegation::{Delegatee, StakingDelegationSupport}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index c2f7ae8074f9..a3e0f58b1a66 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -21,7 +21,7 @@ use super::*; use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use pallet_staking::Error as StakingError; -use sp_staking::delegation::{StakeBalanceType, StakingDelegationSupport}; +use sp_staking::delegation::StakingDelegationSupport; #[test] fn create_a_delegatee_with_first_delegator() { @@ -37,7 +37,7 @@ fn create_a_delegatee_with_first_delegator() { assert_ok!(DelegatedStaking::delegate(fund(&delegator, 1000), &delegatee, 100)); // verify - assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); + assert!(DelegatedStaking::is_delegatee(&delegatee)); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100); }); @@ -77,7 +77,7 @@ fn create_multiple_delegators() { let reward_account: AccountId = 201; // before becoming a delegatee, stakeable balance is only direct balance. - assert_eq!(DelegatedStaking::stake_type(fund(&delegatee, 1000)), StakeBalanceType::Direct); + assert!(!DelegatedStaking::is_delegatee(fund(&delegatee, 1000))); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 1000); // set intention to accept delegation. @@ -95,7 +95,7 @@ fn create_multiple_delegators() { } // verify - assert_eq!(DelegatedStaking::stake_type(&delegatee), StakeBalanceType::Delegated); + assert!(DelegatedStaking::is_delegatee(&delegatee)); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100 * 100); }); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index aa0a11598d95..0c50022fc72f 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1868,10 +1868,12 @@ impl StakingHoldProvider for NoDelegation { Pallet::::release(who) } } + impl StakingDelegationSupport for NoDelegation { fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { T::Currency::free_balance(who) } + fn is_delegatee(_who: &Self::AccountId) -> bool { false } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index a956824df066..6c0ced227a95 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -114,24 +114,8 @@ pub trait Delegatee { delegator_to: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; -} - -/// Allows an account to delegate their stakes to a delegatee. -pub trait Delegator { - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - - /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; - /// Delegate some funds to a Delegatee + /// As a `delegator`, delegate some funds to a Delegatee fn delegate( delegator: &Self::AccountId, delegatee: &Self::AccountId, @@ -154,13 +138,5 @@ pub trait StakingDelegationSupport: StakingHoldProvider { } #[cfg(feature = "std")] - fn stake_type(_who: &Self::AccountId) -> StakeBalanceType { - StakeBalanceType::Direct - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum StakeBalanceType { - Direct, - Delegated, -} + fn is_delegatee(_who: &Self::AccountId) -> bool; +} \ No newline at end of file From 54bebb3e53d866ff5eda799c15e1ece3e2eae3d0 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 7 Dec 2023 00:14:24 +0100 Subject: [PATCH 047/202] rename traits --- substrate/frame/delegated-staking/src/lib.rs | 9 ++++----- substrate/frame/delegated-staking/src/mock.rs | 2 +- substrate/frame/staking/src/ledger.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 12 +++++++----- substrate/primitives/staking/src/delegation.rs | 4 ++-- substrate/primitives/staking/src/lib.rs | 2 +- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d4129b103faa..eec0f407d127 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -39,7 +39,7 @@ use frame_support::{ use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ - delegation::{Delegatee, StakingDelegationSupport}, + delegation::{DelegationInterface, StakingDelegationSupport}, EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -179,7 +179,7 @@ impl DelegationRegister { } } -impl Delegatee for Pallet { +impl DelegationInterface for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -383,7 +383,6 @@ impl Delegatee for Pallet { Ok(()) } - } impl sp_staking::StakingHoldProvider for Pallet { @@ -407,9 +406,9 @@ impl sp_staking::StakingHoldProvider for Pallet { Ok(()) } - fn release(who: &Self::AccountId) { + fn release_all(who: &Self::AccountId) { if !Self::is_delegatee(who) { - T::CoreStaking::release(who); + T::CoreStaking::release_all(who); } let _delegation_register = >::get(who); diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 9e80ee3da371..d77862c5157f 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -30,7 +30,7 @@ use frame_election_provider_support::{ onchain, SequentialPhragmen, }; use pallet_staking::CurrentEra; -use sp_staking::delegation::{Delegatee, StakingDelegationSupport}; +use sp_staking::delegation::{DelegationInterface, StakingDelegationSupport}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 9a4f2ade3f11..962353b04e7f 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -197,7 +197,7 @@ impl StakingLedger { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { - T::DelegationSupport::release(&ledger.stash); + T::DelegationSupport::release_all(&ledger.stash); Ledger::::remove(controller); >::remove(&stash); diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 0c50022fc72f..4dda02320f0f 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1834,7 +1834,7 @@ impl StakingInterface for Pallet { } fn force_unlock(who: &Self::AccountId) -> sp_runtime::DispatchResult { - T::DelegationSupport::release(who); + T::DelegationSupport::release_all(who); Ok(()) } } @@ -1848,7 +1848,7 @@ impl StakingHoldProvider for Pallet { Ok(()) } - fn release(who: &Self::AccountId) { + fn release_all(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } } @@ -1864,8 +1864,8 @@ impl StakingHoldProvider for NoDelegation { Pallet::::update_hold(who, amount) } - fn release(who: &Self::AccountId) { - Pallet::::release(who) + fn release_all(who: &Self::AccountId) { + Pallet::::release_all(who) } } @@ -1873,7 +1873,9 @@ impl StakingDelegationSupport for NoDelegation { fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { T::Currency::free_balance(who) } - fn is_delegatee(_who: &Self::AccountId) -> bool { false } + fn is_delegatee(_who: &Self::AccountId) -> bool { + false + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 6c0ced227a95..c736a3a7a28a 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -22,7 +22,7 @@ use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; /// Allows an account to accept stake delegations and manage its operations. -pub trait Delegatee { +pub trait DelegationInterface { /// Balance type used by the staking system. type Balance: Sub + Ord @@ -139,4 +139,4 @@ pub trait StakingDelegationSupport: StakingHoldProvider { #[cfg(feature = "std")] fn is_delegatee(_who: &Self::AccountId) -> bool; -} \ No newline at end of file +} diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 5bbe49fa8f21..5f04c5e36eec 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -440,6 +440,6 @@ pub trait StakingHoldProvider { fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; /// Release all amount held for stake. - fn release(who: &Self::AccountId); + fn release_all(who: &Self::AccountId); } sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From 50f051a6a171540b404d46c723ccbe4a89e664eb Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 7 Dec 2023 00:19:47 +0100 Subject: [PATCH 048/202] add events for delegated and withdrawn --- substrate/frame/delegated-staking/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index eec0f407d127..086ce09ad0b4 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -381,6 +381,8 @@ impl DelegationInterface for Pallet { T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; + Self::deposit_event(Event::::Delegated { delegatee: delegatee.clone(), delegator: delegator.clone(), amount: value }); + Ok(()) } } @@ -695,6 +697,7 @@ impl Pallet { )?; defensive_assert!(released == value, "hold should have been released fully"); + Self::deposit_event(Event::::Withdrawn { delegatee: delegatee.clone(), delegator: delegator.clone(), amount: value }); Ok(()) } From c159d109159003c6051174636b5165a0d1439ff6 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 7 Dec 2023 00:22:21 +0100 Subject: [PATCH 049/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 086ce09ad0b4..f66d4f84e100 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -381,7 +381,11 @@ impl DelegationInterface for Pallet { T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; - Self::deposit_event(Event::::Delegated { delegatee: delegatee.clone(), delegator: delegator.clone(), amount: value }); + Self::deposit_event(Event::::Delegated { + delegatee: delegatee.clone(), + delegator: delegator.clone(), + amount: value, + }); Ok(()) } @@ -697,7 +701,11 @@ impl Pallet { )?; defensive_assert!(released == value, "hold should have been released fully"); - Self::deposit_event(Event::::Withdrawn { delegatee: delegatee.clone(), delegator: delegator.clone(), amount: value }); + Self::deposit_event(Event::::Withdrawn { + delegatee: delegatee.clone(), + delegator: delegator.clone(), + amount: value, + }); Ok(()) } From 8cb22c7c683934c71c4cb1d4e006e6abebef9e70 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 7 Dec 2023 00:43:52 +0100 Subject: [PATCH 050/202] migrate delegator docs --- substrate/frame/delegated-staking/src/lib.rs | 28 +++++++++++-------- substrate/frame/staking/src/pallet/impls.rs | 5 ---- .../primitives/staking/src/delegation.rs | 10 ++----- substrate/primitives/staking/src/lib.rs | 3 -- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index f66d4f84e100..8710688b2795 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -40,7 +40,7 @@ use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, - EraIndex, Stake, StakerStatus, StakingInterface, + EraIndex, Stake, StakerStatus, StakingInterface, StakingHoldProvider, }; use sp_std::{convert::TryInto, prelude::*}; @@ -98,6 +98,8 @@ pub mod pallet { WithdrawFailed, /// This operation is not supported with Delegation Staking. NotSupported, + /// This delegatee is not set as a migrating account. + NotMigrating, } /// A reason for placing a hold on funds. @@ -136,6 +138,14 @@ pub mod pallet { #[pallet::storage] pub(crate) type Delegatees = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationRegister, OptionQuery>; + + /// Map of Delegatee and its proxy delegator account while its actual delegators are migrating. + /// + /// Helps ensure correctness of ongoing migration of a direct nominator to a delegatee. If a + /// delegatee does not exist, it implies it is not going through migration. + #[pallet::storage] + pub(crate) type DelegateeMigration = + CountedStorageMap<_, Twox64Concat, T::AccountId, T::AccountId, OptionQuery>; } /// Register of all delegations to a `Delegatee`. @@ -251,10 +261,11 @@ impl DelegationInterface for Pallet { _ => return Err(Error::::InvalidDelegation.into()), } + >::insert(&new_delegatee, &proxy_delegator); let stake = T::CoreStaking::stake(new_delegatee)?; // unlock funds from staker - T::CoreStaking::force_unlock(new_delegatee)?; + T::CoreStaking::release_all(new_delegatee); // try transferring the staked amount. This should never fail but if it does, it indicates // bad state and we abort. @@ -326,7 +337,6 @@ impl DelegationInterface for Pallet { #[transactional] fn migrate_delegator( delegatee: &Self::AccountId, - existing_delegator: &Self::AccountId, new_delegator: &Self::AccountId, value: Self::Balance, ) -> DispatchResult { @@ -334,12 +344,13 @@ impl DelegationInterface for Pallet { // ensure delegatee exists. ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); + let proxy_delegator = >::get(delegatee).ok_or(Error::::NotMigrating)?; - // remove delegation of `value` from `existing_delegator`. - Self::delegation_withdraw(existing_delegator, delegatee, value)?; + // remove delegation of `value` from `proxy_delegator`. + Self::delegation_withdraw(&proxy_delegator, delegatee, value)?; // transfer the withdrawn value to `new_delegator`. - T::Currency::transfer(existing_delegator, new_delegator, value, Preservation::Expendable) + T::Currency::transfer(&proxy_delegator, new_delegator, value, Preservation::Expendable) .map_err(|_| Error::::BadState)?; // add the above removed delegation to `new_delegator`. @@ -626,11 +637,6 @@ impl StakingInterface for Pallet { T::CoreStaking::nominations(who) } - fn force_unlock(_who: &Self::AccountId) -> DispatchResult { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - Err(Error::::NotSupported.into()) - } - #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> sp_staking::Page { T::CoreStaking::max_exposure_page_size() diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 4dda02320f0f..40006537c2e3 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1832,11 +1832,6 @@ impl StakingInterface for Pallet { T::MaxExposurePageSize::get() } } - - fn force_unlock(who: &Self::AccountId) -> sp_runtime::DispatchResult { - T::DelegationSupport::release_all(who); - Ok(()) - } } impl StakingHoldProvider for Pallet { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index c736a3a7a28a..0da13c738e15 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -100,18 +100,14 @@ pub trait DelegationInterface { reporter: Option, ) -> DispatchResult; - /// Swap a delegated `value` from `delegator_from` to `delegator_to`, with delegatee remaining - /// the same. + /// Move a delegated amount from `proxy_delegator` to `new_delegator`. /// + /// Delegatee must have used [`Self::migrate_accept_delegations`] to setup a `proxy_delegator`. /// This is useful for migrating old pool accounts using direct staking to lazily move /// delegators to the new delegated pool account. - /// - /// FIXME(ank4n): delegator_from should be removed and be always `proxy_delegator` that was - /// registered while calling [`Self::migrate_accept_delegations`]. fn migrate_delegator( delegatee: &Self::AccountId, - delegator_from: &Self::AccountId, - delegator_to: &Self::AccountId, + new_delegator: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 5f04c5e36eec..c32145f8c563 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -285,9 +285,6 @@ pub trait StakingInterface { } } - /// Used for migration of locks. - fn force_unlock(who: &Self::AccountId) -> DispatchResult; - #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> Page; From 39f435f48e033fab64a047d627392bbaf26beb57 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 7 Dec 2023 00:47:06 +0100 Subject: [PATCH 051/202] empty tests --- substrate/frame/delegated-staking/src/tests.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index a3e0f58b1a66..6dba90ee8725 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -252,7 +252,12 @@ mod integration { } #[test] - fn reward_destination_cannot_be_delegatee() { + fn direct_withdraw_ends_up_in_limbo() { + + } + + #[test] + fn reward_destination_restrictions() { ExtBuilder::default().build_and_execute(|| assert!(true)); } From 3172da679e75f26fbcc125b45ee2be1e8ecccadb Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 01:13:46 +0100 Subject: [PATCH 052/202] refactor and additional tests --- substrate/frame/delegated-staking/src/lib.rs | 61 +++++++++---------- .../frame/delegated-staking/src/tests.rs | 42 ++++++++++++- 2 files changed, 69 insertions(+), 34 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 8710688b2795..cf99a5e96f85 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -316,10 +316,18 @@ impl DelegationInterface for Pallet { value: Self::Balance, num_slashing_spans: u32, ) -> DispatchResult { - // fixme(ank4n) handle killing of stash - let _stash_killed: bool = - T::CoreStaking::withdraw_exact(delegatee, value, num_slashing_spans) - .map_err(|_| Error::::WithdrawFailed)?; + // check how much is already unbonded + let delegation_register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + let unbonded_balance = delegation_register.unbonded_balance(); + + if unbonded_balance < value { + // fixme(ank4n) handle killing of stash + let amount_to_withdraw = value.saturating_sub(unbonded_balance); + let _stash_killed: bool = + T::CoreStaking::withdraw_exact(delegatee, amount_to_withdraw, num_slashing_spans) + .map_err(|_| Error::::WithdrawFailed)?; + } + Self::delegation_withdraw(delegator, delegatee, value) } @@ -369,7 +377,7 @@ impl DelegationInterface for Pallet { ensure!(delegatee != delegator, Error::::InvalidDelegation); ensure!(>::contains_key(delegatee), Error::::NotDelegatee); - // cannot delegate to another delegatee. + // A delegatee cannot delegate. if >::contains_key(delegator) { return Err(Error::::InvalidDelegation.into()) } @@ -670,34 +678,25 @@ impl Pallet { delegatee: &T::AccountId, value: BalanceOf, ) -> DispatchResult { - >::mutate_exists(delegator, |maybe_delegate| match maybe_delegate { - Some((current_delegatee, delegate_balance)) => { - ensure!(¤t_delegatee.clone() == delegatee, Error::::NotDelegatee); - ensure!(*delegate_balance >= value, Error::::NotEnoughFunds); - delegate_balance.saturating_reduce(value); + let mut delegation_register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + ensure!(delegation_register.unbonded_balance() >= value, Error::::BadState); - if *delegate_balance == BalanceOf::::zero() { - *maybe_delegate = None; - } - Ok(()) - }, - None => { - // delegator does not exist - return Err(Error::::NotDelegator) - }, - })?; - - >::mutate(delegatee, |maybe_register| match maybe_register { - Some(ledger) => { - ledger.total_delegated.saturating_reduce(value); - Ok(()) - }, - None => { - // Delegatee not found - return Err(Error::::NotDelegatee) - }, - })?; + delegation_register.total_delegated.saturating_reduce(value); + >::insert(delegatee, delegation_register); + + let (assigned_delegatee, delegate_balance) = >::get(delegator).ok_or(Error::::NotDelegator)?; + // delegator should already be delegating to delegatee + ensure!(&assigned_delegatee == delegatee, Error::::NotDelegatee); + ensure!(delegate_balance >= value, Error::::NotEnoughFunds); + let updated_delegate_balance = delegate_balance.saturating_sub(value); + + // remove delegator if nothing delegated anymore + if updated_delegate_balance == BalanceOf::::zero() { + >::remove(delegator); + } else { + >::insert(delegator, (delegatee, updated_delegate_balance)); + } let released = T::Currency::release( &HoldReason::Delegating.into(), diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 6dba90ee8725..a4e69592d695 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -101,9 +101,29 @@ fn create_multiple_delegators() { } #[test] -fn withdraw_delegation() { +fn delegate_restrictions() { // Similar to creating a nomination pool - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + let delegatee_one = 200; + let delegator_one = 210; + assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee_one, 100), &(delegatee_one+1))); + assert_ok!(DelegatedStaking::delegate(fund(&delegator_one, 200), &delegatee_one, 100)); + + let delegatee_two = 300; + let delegator_two = 310; + assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee_two, 100), &(delegatee_two+1))); + assert_ok!(DelegatedStaking::delegate(fund(&delegator_two, 200), &delegatee_two, 100)); + + // delegatee one tries to delegate to delegatee 2 + assert_noop!(DelegatedStaking::delegate(&delegatee_one, &delegatee_two, 10), Error::::InvalidDelegation); + + // delegatee one tries to delegate to a delegator + assert_noop!(DelegatedStaking::delegate(&delegatee_one, &delegator_one, 10), Error::::NotDelegatee); + assert_noop!(DelegatedStaking::delegate(&delegatee_one, &delegator_two, 10), Error::::NotDelegatee); + + // delegator one tries to delegate to delegatee 2 as well (it already delegates to delegatee 1) + assert_noop!(DelegatedStaking::delegate(&delegator_one, &delegatee_two, 10), Error::::InvalidDelegation); + }); } #[test] @@ -252,8 +272,24 @@ mod integration { } #[test] - fn direct_withdraw_ends_up_in_limbo() { + fn withdraw_happens_with_unbonded_balance_first() { + ExtBuilder::default().build_and_execute(|| { + let delegatee = 200; + setup_delegation_stake(delegatee, 201, (300..350).collect(), 100, 0); + + // verify withdraw not possible yet + assert_noop!(DelegatedStaking::withdraw(&300, &delegatee, 100, 0), Error::::WithdrawFailed); + + // add new delegation that is not staked + assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &delegatee, 100)); + // verify unbonded balance + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); + + // withdraw works now without unbonding + assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 100, 0)); + assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + }); } #[test] From d469759a2d6cd753fffb4d724718962f6147c8c8 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 01:48:43 +0100 Subject: [PATCH 053/202] add todos in tests --- substrate/frame/delegated-staking/src/lib.rs | 15 +++--- .../frame/delegated-staking/src/tests.rs | 52 +++++++++++++++---- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index cf99a5e96f85..43559a3dbdd5 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -40,7 +40,7 @@ use pallet::*; use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, - EraIndex, Stake, StakerStatus, StakingInterface, StakingHoldProvider, + EraIndex, Stake, StakerStatus, StakingHoldProvider, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -317,7 +317,8 @@ impl DelegationInterface for Pallet { num_slashing_spans: u32, ) -> DispatchResult { // check how much is already unbonded - let delegation_register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + let delegation_register = + >::get(delegatee).ok_or(Error::::NotDelegatee)?; let unbonded_balance = delegation_register.unbonded_balance(); if unbonded_balance < value { @@ -352,7 +353,8 @@ impl DelegationInterface for Pallet { // ensure delegatee exists. ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); - let proxy_delegator = >::get(delegatee).ok_or(Error::::NotMigrating)?; + let proxy_delegator = + >::get(delegatee).ok_or(Error::::NotMigrating)?; // remove delegation of `value` from `proxy_delegator`. Self::delegation_withdraw(&proxy_delegator, delegatee, value)?; @@ -678,14 +680,15 @@ impl Pallet { delegatee: &T::AccountId, value: BalanceOf, ) -> DispatchResult { - - let mut delegation_register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + let mut delegation_register = + >::get(delegatee).ok_or(Error::::NotDelegatee)?; ensure!(delegation_register.unbonded_balance() >= value, Error::::BadState); delegation_register.total_delegated.saturating_reduce(value); >::insert(delegatee, delegation_register); - let (assigned_delegatee, delegate_balance) = >::get(delegator).ok_or(Error::::NotDelegator)?; + let (assigned_delegatee, delegate_balance) = + >::get(delegator).ok_or(Error::::NotDelegator)?; // delegator should already be delegating to delegatee ensure!(&assigned_delegatee == delegatee, Error::::NotDelegatee); ensure!(delegate_balance >= value, Error::::NotEnoughFunds); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index a4e69592d695..c31fef3e78e2 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -106,34 +106,57 @@ fn delegate_restrictions() { ExtBuilder::default().build_and_execute(|| { let delegatee_one = 200; let delegator_one = 210; - assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee_one, 100), &(delegatee_one+1))); + assert_ok!(DelegatedStaking::accept_delegations( + fund(&delegatee_one, 100), + &(delegatee_one + 1) + )); assert_ok!(DelegatedStaking::delegate(fund(&delegator_one, 200), &delegatee_one, 100)); let delegatee_two = 300; let delegator_two = 310; - assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee_two, 100), &(delegatee_two+1))); + assert_ok!(DelegatedStaking::accept_delegations( + fund(&delegatee_two, 100), + &(delegatee_two + 1) + )); assert_ok!(DelegatedStaking::delegate(fund(&delegator_two, 200), &delegatee_two, 100)); // delegatee one tries to delegate to delegatee 2 - assert_noop!(DelegatedStaking::delegate(&delegatee_one, &delegatee_two, 10), Error::::InvalidDelegation); + assert_noop!( + DelegatedStaking::delegate(&delegatee_one, &delegatee_two, 10), + Error::::InvalidDelegation + ); // delegatee one tries to delegate to a delegator - assert_noop!(DelegatedStaking::delegate(&delegatee_one, &delegator_one, 10), Error::::NotDelegatee); - assert_noop!(DelegatedStaking::delegate(&delegatee_one, &delegator_two, 10), Error::::NotDelegatee); + assert_noop!( + DelegatedStaking::delegate(&delegatee_one, &delegator_one, 10), + Error::::NotDelegatee + ); + assert_noop!( + DelegatedStaking::delegate(&delegatee_one, &delegator_two, 10), + Error::::NotDelegatee + ); - // delegator one tries to delegate to delegatee 2 as well (it already delegates to delegatee 1) - assert_noop!(DelegatedStaking::delegate(&delegator_one, &delegatee_two, 10), Error::::InvalidDelegation); + // delegator one tries to delegate to delegatee 2 as well (it already delegates to delegatee + // 1) + assert_noop!( + DelegatedStaking::delegate(&delegator_one, &delegatee_two, 10), + Error::::InvalidDelegation + ); }); } #[test] fn apply_pending_slash() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + todo!() + }); } #[test] fn distribute_rewards() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + todo!() + }); } /// Integration tests with pallet-staking. @@ -278,7 +301,10 @@ mod integration { setup_delegation_stake(delegatee, 201, (300..350).collect(), 100, 0); // verify withdraw not possible yet - assert_noop!(DelegatedStaking::withdraw(&300, &delegatee, 100, 0), Error::::WithdrawFailed); + assert_noop!( + DelegatedStaking::withdraw(&300, &delegatee, 100, 0), + Error::::WithdrawFailed + ); // add new delegation that is not staked assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &delegatee, 100)); @@ -301,6 +327,7 @@ mod integration { fn nominate_test() { ExtBuilder::default().build_and_execute(|| assert!(true)); } + #[test] fn slash_works() { ExtBuilder::default().build_and_execute(|| { @@ -308,11 +335,14 @@ mod integration { start_era(1); // delegatee is slashed + todo!() }); } #[test] fn migration_works() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + todo!() + }); } } From 430fb6b0538d7f6063c96f43f4e31ef099c0e732 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 03:01:24 +0100 Subject: [PATCH 054/202] reward destination restriction test --- .../frame/delegated-staking/src/tests.rs | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index c31fef3e78e2..1140a6238bc7 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -161,6 +161,7 @@ fn distribute_rewards() { /// Integration tests with pallet-staking. mod integration { + use pallet_staking::RewardDestination; use super::*; use sp_staking::Stake; @@ -320,12 +321,43 @@ mod integration { #[test] fn reward_destination_restrictions() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + ExtBuilder::default().build_and_execute(|| { + // give some funds to 200 + fund(&200, 1000); + let balance_200 = Balances::free_balance(200); + + // delegatee cannot be reward destination + assert_noop!(DelegatedStaking::accept_delegations(&200, &200), Error::::InvalidRewardDestination); + + // different reward account works + assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); + // add some delegations to it + assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &200, 100)); + + + // if delegatee calls Staking pallet directly with a different reward destination, it fails. + assert_noop!(Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Stash), StakingError::::RewardDestinationRestricted); + // non stash account different than one passed to DelegatedStaking also does not work.. + assert_noop!(Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Account(202)), StakingError::::RewardDestinationRestricted); + // passing correct reward destination works + assert_ok!(Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Account(201))); + // amount is staked correctly + assert!(eq_stake(200, 100, 100)); + assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); + assert_eq!(DelegatedStaking::delegated_balance(&200), 100); + + // free balance of delegatee is untouched + assert_eq!(Balances::free_balance(200), balance_200); + }); } #[test] - fn nominate_test() { - ExtBuilder::default().build_and_execute(|| assert!(true)); + fn delegatee_restrictions() { + ExtBuilder::default().build_and_execute(|| { + // delegatee cannot be reward destination + assert_noop!(DelegatedStaking::accept_delegations(fund(&200, 1000), &200), Error::::InvalidRewardDestination); + + }); } #[test] From 6ba4f827f3b00d163788a4f188d61483ebe3b779 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 03:46:42 +0100 Subject: [PATCH 055/202] more tests --- substrate/frame/delegated-staking/src/lib.rs | 67 +++++++------ .../frame/delegated-staking/src/tests.rs | 93 ++++++++++++++----- substrate/frame/staking/src/pallet/mod.rs | 4 +- .../primitives/staking/src/delegation.rs | 5 +- 4 files changed, 113 insertions(+), 56 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 43559a3dbdd5..1624ddc5a4bf 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -100,6 +100,8 @@ pub mod pallet { NotSupported, /// This delegatee is not set as a migrating account. NotMigrating, + /// Delegatee no longer accepting new delegations. + DelegationsBlocked, } /// A reason for placing a hold on funds. @@ -207,30 +209,29 @@ impl DelegationInterface for Pallet { who: &Self::AccountId, reward_destination: &Self::AccountId, ) -> DispatchResult { - // Existing delegatee cannot accept delegation + // Existing delegatee cannot register again. ensure!(!>::contains_key(who), Error::::NotAllowed); + // A delegator cannot become a delegatee. + ensure!(!>::contains_key(who), Error::::NotAllowed); + // make sure they are not already a direct staker ensure!(T::CoreStaking::status(who).is_err(), Error::::AlreadyStaker); // payee account cannot be same as delegatee ensure!(reward_destination != who, Error::::InvalidRewardDestination); - // if already a delegator, unblock and return success - >::mutate(who, |maybe_register| { - if let Some(register) = maybe_register { - register.blocked = false; - register.payee = reward_destination.clone(); - } else { - *maybe_register = Some(DelegationRegister { - payee: reward_destination.clone(), - total_delegated: Zero::zero(), - hold: Zero::zero(), - pending_slash: Zero::zero(), - blocked: false, - }); - } - }); + // already checked delegatees exist + >::insert( + who, + DelegationRegister { + payee: reward_destination.clone(), + total_delegated: Zero::zero(), + hold: Zero::zero(), + pending_slash: Zero::zero(), + blocked: false, + }, + ); Ok(()) } @@ -283,14 +284,19 @@ impl DelegationInterface for Pallet { } fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult { - >::mutate(delegatee, |maybe_register| { - if let Some(register) = maybe_register { - register.blocked = true; - Ok(()) - } else { - Err(Error::::NotDelegatee.into()) - } - }) + let mut register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + register.blocked = true; + >::insert(delegatee, register); + + Ok(()) + } + + fn unblock_delegations(delegatee: &Self::AccountId) -> DispatchResult { + let mut register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + register.blocked = false; + >::insert(delegatee, register); + + Ok(()) } fn kill_delegatee(_delegatee: &Self::AccountId) -> DispatchResult { @@ -377,7 +383,10 @@ impl DelegationInterface for Pallet { ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); ensure!(delegator_balance >= value, Error::::NotEnoughFunds); ensure!(delegatee != delegator, Error::::InvalidDelegation); - ensure!(>::contains_key(delegatee), Error::::NotDelegatee); + + let mut delegation_register = + >::get(delegatee).ok_or(Error::::NotDelegatee)?; + ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); // A delegatee cannot delegate. if >::contains_key(delegator) { @@ -393,12 +402,10 @@ impl DelegationInterface for Pallet { value }; + delegation_register.total_delegated.saturating_accrue(value); + >::insert(delegator, (delegatee, new_delegation_amount)); - >::mutate(delegatee, |maybe_register| { - if let Some(register) = maybe_register { - register.total_delegated.saturating_accrue(value); - } - }); + >::insert(delegatee, delegation_register); T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 1140a6238bc7..9c77a137922e 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -147,22 +147,13 @@ fn delegate_restrictions() { #[test] fn apply_pending_slash() { - ExtBuilder::default().build_and_execute(|| { - todo!() - }); -} - -#[test] -fn distribute_rewards() { - ExtBuilder::default().build_and_execute(|| { - todo!() - }); + ExtBuilder::default().build_and_execute(|| todo!()); } /// Integration tests with pallet-staking. mod integration { - use pallet_staking::RewardDestination; use super::*; + use pallet_staking::RewardDestination; use sp_staking::Stake; #[test] @@ -327,20 +318,33 @@ mod integration { let balance_200 = Balances::free_balance(200); // delegatee cannot be reward destination - assert_noop!(DelegatedStaking::accept_delegations(&200, &200), Error::::InvalidRewardDestination); + assert_noop!( + DelegatedStaking::accept_delegations(&200, &200), + Error::::InvalidRewardDestination + ); // different reward account works assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); // add some delegations to it assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &200, 100)); - - // if delegatee calls Staking pallet directly with a different reward destination, it fails. - assert_noop!(Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Stash), StakingError::::RewardDestinationRestricted); + // if delegatee calls Staking pallet directly with a different reward destination, it + // fails. + assert_noop!( + Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Stash), + StakingError::::RewardDestinationRestricted + ); // non stash account different than one passed to DelegatedStaking also does not work.. - assert_noop!(Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Account(202)), StakingError::::RewardDestinationRestricted); + assert_noop!( + Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Account(202)), + StakingError::::RewardDestinationRestricted + ); // passing correct reward destination works - assert_ok!(Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Account(201))); + assert_ok!(Staking::bond( + RuntimeOrigin::signed(200), + 100, + RewardDestination::Account(201) + )); // amount is staked correctly assert!(eq_stake(200, 100, 100)); assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); @@ -348,15 +352,62 @@ mod integration { // free balance of delegatee is untouched assert_eq!(Balances::free_balance(200), balance_200); + + // trying to change reward destination later directly via staking does not work. + assert_noop!( + Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Staked), + StakingError::::RewardDestinationRestricted + ); + assert_noop!( + Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Account(300)), + StakingError::::RewardDestinationRestricted + ); }); } #[test] fn delegatee_restrictions() { ExtBuilder::default().build_and_execute(|| { - // delegatee cannot be reward destination - assert_noop!(DelegatedStaking::accept_delegations(fund(&200, 1000), &200), Error::::InvalidRewardDestination); + setup_delegation_stake(200, 201, (202..203).collect(), 100, 0); + + // Registering again is noop + assert_noop!(DelegatedStaking::accept_delegations(&200, &201), Error::::NotAllowed); + // a delegator cannot become delegatee + assert_noop!(DelegatedStaking::accept_delegations(&202, &203), Error::::NotAllowed); + // existing staker cannot become a delegatee + assert_noop!( + DelegatedStaking::accept_delegations(&GENESIS_NOMINATOR_ONE, &201), + Error::::AlreadyStaker + ); + assert_noop!( + DelegatedStaking::accept_delegations(&GENESIS_VALIDATOR, &201), + Error::::AlreadyStaker + ); + }); + } + + #[test] + fn block_delegations() { + ExtBuilder::default().build_and_execute(|| { + assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); + + // delegation works + assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &200, 100)); + // delegatee blocks delegation + assert_ok!(DelegatedStaking::block_delegations(&200)); + + // cannot delegate to it anymore + assert_noop!( + DelegatedStaking::delegate(&300, &200, 100), + Error::::DelegationsBlocked + ); + + // delegatee can unblock delegation + assert_ok!(DelegatedStaking::unblock_delegations(&200)); + + // delegation works again + assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); }); } @@ -373,8 +424,6 @@ mod integration { #[test] fn migration_works() { - ExtBuilder::default().build_and_execute(|| { - todo!() - }); + ExtBuilder::default().build_and_execute(|| todo!()); } } diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 13f357e39e80..fa154e6440e4 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -1317,9 +1317,7 @@ pub mod pallet { Error::::ControllerDeprecated ); - let _ = ledger - .set_payee(payee) - .defensive_proof("ledger was retrieved from storage, thus its bonded; qed.")?; + ledger.set_payee(payee)?; Ok(()) } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 0da13c738e15..7109671cbdf2 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -68,9 +68,12 @@ pub trait DelegationInterface { payee: &Self::AccountId, ) -> DispatchResult; - /// Stop accepting new delegations on this account. + /// Stop accepting new delegations to this account. fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; + /// Unblock delegations to this account. + fn unblock_delegations(delegatee: &Self::AccountId) -> DispatchResult; + /// Remove oneself as Delegatee. /// /// This will only succeed if all delegations to this delegatee are withdrawn. From 071450cbe864378e3c27f1746792c7aee806a31d Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 05:18:55 +0100 Subject: [PATCH 056/202] add migration test --- substrate/frame/delegated-staking/src/lib.rs | 56 ++++++++++++--- .../frame/delegated-staking/src/tests.rs | 70 ++++++++++++++++++- 2 files changed, 116 insertions(+), 10 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1624ddc5a4bf..d1647e181168 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -215,12 +215,12 @@ impl DelegationInterface for Pallet { // A delegator cannot become a delegatee. ensure!(!>::contains_key(who), Error::::NotAllowed); - // make sure they are not already a direct staker - ensure!(T::CoreStaking::status(who).is_err(), Error::::AlreadyStaker); - // payee account cannot be same as delegatee ensure!(reward_destination != who, Error::::InvalidRewardDestination); + // make sure they are not already a direct staker or they are migrating. + ensure!(T::CoreStaking::status(who).is_err() || >::contains_key(who), Error::::AlreadyStaker); + // already checked delegatees exist >::insert( who, @@ -280,7 +280,10 @@ impl DelegationInterface for Pallet { // delegate from new delegator to staker. Self::accept_delegations(new_delegatee, payee)?; - Self::delegate(proxy_delegator, new_delegatee, stake.total) + // todo(ank4n): for existing nominators, how this payee should be propagated to CoreStaking? + + Self::delegate(proxy_delegator, new_delegatee, stake.total)?; + Self::update_bond(new_delegatee) } fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult { @@ -356,21 +359,52 @@ impl DelegationInterface for Pallet { value: Self::Balance, ) -> DispatchResult { ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - - // ensure delegatee exists. - ensure!(!>::contains_key(delegatee), Error::::NotDelegatee); + // make sure new delegator is not an existing delegator or a delegatee + ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); + ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); + // ensure we are migrating let proxy_delegator = >::get(delegatee).ok_or(Error::::NotMigrating)?; + // proxy delegator must exist + let (assigned_delegatee, delegate_balance) = + >::get(&proxy_delegator).ok_or(Error::::BadState)?; + ensure!(assigned_delegatee == *delegatee, Error::::BadState); + + // make sure proxy delegator has enough balance to support this migration. + ensure!(delegate_balance >= value, Error::::NotEnoughFunds); // remove delegation of `value` from `proxy_delegator`. - Self::delegation_withdraw(&proxy_delegator, delegatee, value)?; + let updated_delegate_balance = delegate_balance.saturating_sub(value); + + // if all funds are migrated out of proxy delegator, clean up. + if updated_delegate_balance == BalanceOf::::zero() { + >::remove(&proxy_delegator); + >::remove(delegatee); + } else { + // else update proxy delegator + >::insert(&proxy_delegator, (delegatee, updated_delegate_balance)); + } + + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &proxy_delegator, + value, + Precision::BestEffort, + )?; + + defensive_assert!(released == value, "hold should have been released fully"); // transfer the withdrawn value to `new_delegator`. T::Currency::transfer(&proxy_delegator, new_delegator, value, Preservation::Expendable) .map_err(|_| Error::::BadState)?; + // add the above removed delegation to `new_delegator`. - Self::delegate(new_delegator, delegatee, value) + >::insert(new_delegator, (delegatee, value)); + // hold the funds again in the new delegator account. + T::Currency::hold(&HoldReason::Delegating.into(), &new_delegator, value)?; + + Ok(()) } fn delegate( @@ -682,6 +716,10 @@ impl Pallet { >::contains_key(who) } + fn is_migrating(delegatee: &T::AccountId) -> bool { + >::contains_key(delegatee) + } + fn delegation_withdraw( delegator: &T::AccountId, delegatee: &T::AccountId, diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 9c77a137922e..e89b670f02bd 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -424,6 +424,74 @@ mod integration { #[test] fn migration_works() { - ExtBuilder::default().build_and_execute(|| todo!()); + ExtBuilder::default().build_and_execute(|| { + // add a nominator + fund(&200, 5000); + let staked_amount = 4000; + assert_ok!(Staking::bond( + RuntimeOrigin::signed(200), + staked_amount, + RewardDestination::Account(201) + )); + assert_ok!(Staking::nominate( + RuntimeOrigin::signed(200), + vec![GENESIS_VALIDATOR], + )); + let init_stake = Staking::stake(&200).unwrap(); + + // scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304) + // in equal parts. lets try to migrate this nominator into delegatee based stake. + + // all balance currently is in 200 + assert_eq!(Balances::free_balance(200), 5000); + + // to migrate, nominator needs to set an account as a proxy delegator where staked funds + // will be moved and delegated back to this old nominator account. This should be funded + // with at least ED. + let proxy_delegator = fund(&202, ExistentialDeposit::get()); + + assert_ok!(DelegatedStaking::migrate_accept_delegations(&200, &proxy_delegator, &201)); + assert!(DelegatedStaking::is_migrating(&200)); + + // verify all went well + let mut expected_proxy_delegated_amount = staked_amount; + assert_eq!( + Balances::balance_on_hold(&HoldReason::Delegating.into(), &proxy_delegator), + expected_proxy_delegated_amount + ); + assert_eq!(Balances::free_balance(200), 5000 - staked_amount); + assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); + assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); + assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); + + // now lets migrate the delegators + let delegator_share = staked_amount/4; + for delegator in 300..304 { + assert_eq!(Balances::free_balance(delegator), 0); + // fund them with ED + fund(&delegator, ExistentialDeposit::get()); + // migrate 1/4th amount into each delegator + assert_ok!(DelegatedStaking::migrate_delegator(&200, &delegator, delegator_share)); + assert_eq!( + Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), + delegator_share + ); + expected_proxy_delegated_amount -= delegator_share; + assert_eq!( + Balances::balance_on_hold(&HoldReason::Delegating.into(), &proxy_delegator), + expected_proxy_delegated_amount + ); + + // delegatee stake is unchanged. + assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); + assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); + assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); + } + + assert!(!DelegatedStaking::is_migrating(&200)); + + // cannot use migrate delegator anymore + assert_noop!(DelegatedStaking::migrate_delegator(&200, &305, 1), Error::::NotMigrating); + }); } } From 6cf4a35433c9060c32cb32a2bdad4021da2296db Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 05:19:30 +0100 Subject: [PATCH 057/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 6 ++++-- substrate/frame/delegated-staking/src/tests.rs | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d1647e181168..d5ae4d546eae 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -219,7 +219,10 @@ impl DelegationInterface for Pallet { ensure!(reward_destination != who, Error::::InvalidRewardDestination); // make sure they are not already a direct staker or they are migrating. - ensure!(T::CoreStaking::status(who).is_err() || >::contains_key(who), Error::::AlreadyStaker); + ensure!( + T::CoreStaking::status(who).is_err() || >::contains_key(who), + Error::::AlreadyStaker + ); // already checked delegatees exist >::insert( @@ -398,7 +401,6 @@ impl DelegationInterface for Pallet { T::Currency::transfer(&proxy_delegator, new_delegator, value, Preservation::Expendable) .map_err(|_| Error::::BadState)?; - // add the above removed delegation to `new_delegator`. >::insert(new_delegator, (delegatee, value)); // hold the funds again in the new delegator account. diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index e89b670f02bd..51c949d05bd2 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -433,10 +433,7 @@ mod integration { staked_amount, RewardDestination::Account(201) )); - assert_ok!(Staking::nominate( - RuntimeOrigin::signed(200), - vec![GENESIS_VALIDATOR], - )); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(200), vec![GENESIS_VALIDATOR],)); let init_stake = Staking::stake(&200).unwrap(); // scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304) @@ -465,7 +462,7 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); // now lets migrate the delegators - let delegator_share = staked_amount/4; + let delegator_share = staked_amount / 4; for delegator in 300..304 { assert_eq!(Balances::free_balance(delegator), 0); // fund them with ED @@ -491,7 +488,10 @@ mod integration { assert!(!DelegatedStaking::is_migrating(&200)); // cannot use migrate delegator anymore - assert_noop!(DelegatedStaking::migrate_delegator(&200, &305, 1), Error::::NotMigrating); + assert_noop!( + DelegatedStaking::migrate_delegator(&200, &305, 1), + Error::::NotMigrating + ); }); } } From b1249e0ea9fd481af0514ff5f1e2e8d6c3aed257 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 8 Dec 2023 22:11:32 +0100 Subject: [PATCH 058/202] todo --- substrate/frame/delegated-staking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d5ae4d546eae..fed1a1fde879 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -282,8 +282,8 @@ impl DelegationInterface for Pallet { .map_err(|_| Error::::BadState)?; // delegate from new delegator to staker. + // todo(ank4n) : inline this fn and propagate payee to core staking.. Self::accept_delegations(new_delegatee, payee)?; - // todo(ank4n): for existing nominators, how this payee should be propagated to CoreStaking? Self::delegate(proxy_delegator, new_delegatee, stake.total)?; Self::update_bond(new_delegatee) From 066cdec1a76d0bb320c56e1ab3140bd8873b4eea Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 00:42:24 +0100 Subject: [PATCH 059/202] is_delegatee for non_tests --- substrate/frame/delegated-staking/src/lib.rs | 12 +++--------- substrate/primitives/staking/src/delegation.rs | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index fed1a1fde879..7966101a5960 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -66,7 +66,7 @@ pub mod pallet { /// Core staking implementation. type CoreStaking: StakingInterface, AccountId = Self::AccountId> - + sp_staking::StakingHoldProvider, AccountId = Self::AccountId>; + + StakingHoldProvider, AccountId = Self::AccountId>; } #[pallet::error] @@ -184,11 +184,6 @@ impl DelegationRegister { pub fn unbonded_balance(&self) -> BalanceOf { self.total_delegated.saturating_sub(self.hold) } - - /// consumes self and returns Delegation Register with updated hold amount. - pub fn update_hold(self, amount: BalanceOf) -> Self { - DelegationRegister { hold: amount, ..self } - } } impl DelegationInterface for Pallet { @@ -455,7 +450,7 @@ impl DelegationInterface for Pallet { } } -impl sp_staking::StakingHoldProvider for Pallet { +impl StakingHoldProvider for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -470,7 +465,7 @@ impl sp_staking::StakingHoldProvider for Pallet { ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = delegation_register.update_hold(amount); + let updated_register = DelegationRegister { hold: amount, ..delegation_register }; >::insert(who, updated_register); Ok(()) @@ -516,7 +511,6 @@ impl StakingDelegationSupport for Pallet { register.payee != reward_acc } - #[cfg(feature = "std")] fn is_delegatee(who: &Self::AccountId) -> bool { Self::is_delegatee(who) } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 7109671cbdf2..8d36941d2d1b 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -136,6 +136,5 @@ pub trait StakingDelegationSupport: StakingHoldProvider { false } - #[cfg(feature = "std")] fn is_delegatee(_who: &Self::AccountId) -> bool; } From 1e38b9bcc7b88436d6c53fff697c6b36217b3959 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 01:29:16 +0100 Subject: [PATCH 060/202] staking as orchestrator and everything passes --- substrate/frame/staking/src/ledger.rs | 13 +++---- substrate/frame/staking/src/pallet/impls.rs | 40 +++++++++++++++++---- substrate/frame/staking/src/pallet/mod.rs | 9 +++-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 962353b04e7f..55617fe32548 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -18,10 +18,10 @@ //! A Ledger implementation for stakers. use frame_support::defensive; -use sp_staking::{delegation::StakingDelegationSupport, StakingAccount, StakingHoldProvider}; +use sp_staking::{StakingAccount, StakingHoldProvider}; use sp_std::prelude::*; -use crate::{BalanceOf, Bonded, Config, Error, Ledger, Payee, RewardDestination, StakingLedger}; +use crate::{BalanceOf, Bonded, Config, Error, Ledger, Pallet, Payee, RewardDestination, StakingLedger}; #[cfg(any(feature = "runtime-benchmarks", test))] use sp_runtime::traits::Zero; @@ -141,8 +141,9 @@ impl StakingLedger { return Err(Error::::NotStash) } - T::DelegationSupport::update_hold(&self.stash, self.total) + Pallet::::update_hold(&self.stash, self.total) .map_err(|_| Error::::BadState)?; + Ledger::::insert( &self.controller().ok_or_else(|| { defensive!("update called on a ledger that is not bonded."); @@ -162,7 +163,7 @@ impl StakingLedger { return Err(Error::::AlreadyBonded); } - if T::DelegationSupport::restrict_reward_destination( + if Pallet::::restrict_reward_destination( &self.stash, payee.clone().from(&self.stash), ) { @@ -180,7 +181,7 @@ impl StakingLedger { return Err(Error::::NotStash); } - if T::DelegationSupport::restrict_reward_destination( + if Pallet::::restrict_reward_destination( &self.stash, payee.clone().from(&self.stash), ) { @@ -197,7 +198,7 @@ impl StakingLedger { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { - T::DelegationSupport::release_all(&ledger.stash); + Pallet::::release_all(&ledger.stash); Ledger::::remove(controller); >::remove(&stash); diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 40006537c2e3..16c83446fa62 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1095,6 +1095,25 @@ impl Pallet { ) -> Exposure> { EraInfo::::get_full_exposure(era, account) } + + pub(crate) fn stakeable_balance(who: &T::AccountId) -> BalanceOf { + if T::DelegationSupport::is_delegatee(who) { + return T::DelegationSupport::stakeable_balance(who); + } + + T::Currency::free_balance(who) + } + + pub(crate) fn restrict_reward_destination( + who: &T::AccountId, + reward_destination: Option, + ) -> bool { + if T::DelegationSupport::is_delegatee(who) { + return T::DelegationSupport::restrict_reward_destination(who, reward_destination); + } + + false + } } impl Pallet { @@ -1839,11 +1858,19 @@ impl StakingHoldProvider for Pallet { type AccountId = T::AccountId; fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { + if T::DelegationSupport::is_delegatee(who) { + return T::DelegationSupport::update_hold(who, amount); + } + T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); Ok(()) } fn release_all(who: &Self::AccountId) { + if T::DelegationSupport::is_delegatee(who) { + return T::DelegationSupport::release_all(who); + } + T::Currency::remove_lock(crate::STAKING_ID, who) } } @@ -1855,18 +1882,19 @@ impl StakingHoldProvider for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { - Pallet::::update_hold(who, amount) + fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { + defensive_assert!(true, "delegation update_hold should not be called for NoDelegation"); + Err(Error::::NotEnoughFunds.into()) } - fn release_all(who: &Self::AccountId) { - Pallet::::release_all(who) + fn release_all(_who: &Self::AccountId) { + defensive_assert!(true, "delegation release_all should not be called for NoDelegation"); } } impl StakingDelegationSupport for NoDelegation { - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::free_balance(who) + fn stakeable_balance(_who: &Self::AccountId) -> Self::Balance { + BalanceOf::::zero() } fn is_delegatee(_who: &Self::AccountId) -> bool { false diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index fa154e6440e4..b558d5650d89 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -105,8 +105,7 @@ pub mod pallet { + TypeInfo + MaxEncodedLen; - /// Something that provides stakeable balance of an account as well as a way to hold and - /// release this stake. + /// Something that provides delegation support to staking pallet. type DelegationSupport: StakingDelegationSupport< Balance = Self::CurrencyBalance, AccountId = Self::AccountId, @@ -712,7 +711,7 @@ pub mod pallet { status ); assert!( - T::DelegationSupport::stakeable_balance(stash) >= balance, + Pallet::::stakeable_balance(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::bond( @@ -950,7 +949,7 @@ pub mod pallet { frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - let stash_balance = T::DelegationSupport::stakeable_balance(&stash); + let stash_balance = Self::stakeable_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::new(stash.clone(), value); @@ -986,7 +985,7 @@ pub mod pallet { let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; - let stash_balance = T::DelegationSupport::stakeable_balance(&stash); + let stash_balance = Self::stakeable_balance(&stash); if let Some(extra) = stash_balance.checked_sub(&ledger.total) { let extra = extra.min(max_additional); ledger.total += extra; From 22155c0c5d20f9493ef7f1d4488fbde6d88de4cc Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 01:48:42 +0100 Subject: [PATCH 061/202] fix test and refactor delegated staking --- substrate/frame/delegated-staking/src/lib.rs | 39 ++++--------- substrate/frame/delegated-staking/src/mock.rs | 13 ++--- .../frame/delegated-staking/src/tests.rs | 56 ++++++++++--------- substrate/frame/staking/src/ledger.rs | 17 ++---- 4 files changed, 53 insertions(+), 72 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 7966101a5960..326519f8fccd 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -455,9 +455,7 @@ impl StakingHoldProvider for Pallet { type AccountId = T::AccountId; fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - if !Self::is_delegatee(who) { - return T::CoreStaking::update_hold(who, amount); - } + ensure!(Self::is_delegatee(who), Error::::NotSupported); // delegation register should exist since `who` is a delegatee. let delegation_register = @@ -482,10 +480,9 @@ impl StakingHoldProvider for Pallet { } impl StakingDelegationSupport for Pallet { fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who).map_or_else( - || T::Currency::reducible_balance(who, Preservation::Expendable, Fortitude::Polite), - |delegatee| delegatee.delegated_balance(), - ) + >::get(who) + .map(|delegatee| delegatee.delegated_balance()) + .unwrap_or_default() } fn restrict_reward_destination( @@ -549,11 +546,8 @@ impl StakingInterface for Pallet { } fn stake(who: &Self::AccountId) -> Result, DispatchError> { - if Self::is_delegatee(who) { - return T::CoreStaking::stake(who); - } - - Err(Error::::NotSupported.into()) + ensure!(Self::is_delegatee(who), Error::::NotSupported); + return T::CoreStaking::stake(who); } fn total_stake(who: &Self::AccountId) -> Result { @@ -579,11 +573,8 @@ impl StakingInterface for Pallet { } fn fully_unbond(who: &Self::AccountId) -> DispatchResult { - if Self::is_delegatee(who) { - return T::CoreStaking::fully_unbond(who); - } - - Err(Error::::NotSupported.into()) + ensure!(Self::is_delegatee(who), Error::::NotSupported); + return T::CoreStaking::fully_unbond(who); } fn bond( @@ -602,19 +593,13 @@ impl StakingInterface for Pallet { } fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { - if Self::is_delegatee(who) { - return T::CoreStaking::nominate(who, validators); - } - - Err(Error::::NotSupported.into()) + ensure!(Self::is_delegatee(who), Error::::NotSupported); + return T::CoreStaking::nominate(who, validators); } fn chill(who: &Self::AccountId) -> DispatchResult { - if Self::is_delegatee(who) { - return T::CoreStaking::chill(who); - } - - Err(Error::::NotSupported.into()) + ensure!(Self::is_delegatee(who), Error::::NotSupported); + return T::CoreStaking::chill(who); } fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index d77862c5157f..83666cdd1747 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -228,9 +228,8 @@ impl ExtBuilder { } /// fund and return who. -pub(crate) fn fund(who: &AccountId, amount: Balance) -> &AccountId { +pub(crate) fn fund(who: &AccountId, amount: Balance) { let _ = Balances::deposit_creating(who, amount); - who } /// Sets up delegation for passed delegators, returns total delegated amount. @@ -244,17 +243,15 @@ pub(crate) fn setup_delegation_stake( base_delegate_amount: Balance, increment: Balance, ) -> Balance { - assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); + fund(&delegatee, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_acc)); let mut delegated_amount: Balance = 0; for (index, delegator) in delegators.iter().enumerate() { let amount_to_delegate = base_delegate_amount + increment * index as Balance; delegated_amount += amount_to_delegate; - assert_ok!(DelegatedStaking::delegate( - fund(delegator, amount_to_delegate + ExistentialDeposit::get()), - &delegatee, - amount_to_delegate - )); + fund(delegator, amount_to_delegate + ExistentialDeposit::get()); + assert_ok!(DelegatedStaking::delegate(delegator, &delegatee, amount_to_delegate)); assert_ok!(DelegatedStaking::update_bond(&delegatee)); } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 51c949d05bd2..b2f41e68a9c9 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -31,10 +31,12 @@ fn create_a_delegatee_with_first_delegator() { let delegator: AccountId = 202; // set intention to accept delegation. - assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 1000), &reward_account)); + fund(&delegatee, 1000); + assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); // delegate to this account - assert_ok!(DelegatedStaking::delegate(fund(&delegator, 1000), &delegatee, 100)); + fund(&delegator, 1000); + assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); // verify assert!(DelegatedStaking::is_delegatee(&delegatee)); @@ -76,20 +78,18 @@ fn create_multiple_delegators() { let delegatee: AccountId = 200; let reward_account: AccountId = 201; - // before becoming a delegatee, stakeable balance is only direct balance. - assert!(!DelegatedStaking::is_delegatee(fund(&delegatee, 1000))); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 1000); + // stakeable balance is 0 for non delegatee + fund(&delegatee, 1000); + assert!(!DelegatedStaking::is_delegatee(&delegatee)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); // set intention to accept delegation. assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); // create 100 delegators for i in 202..302 { - assert_ok!(DelegatedStaking::delegate( - fund(&i, 100 + ExistentialDeposit::get()), - &delegatee, - 100 - )); + fund(&i, 100 + ExistentialDeposit::get()); + assert_ok!(DelegatedStaking::delegate(&i, &delegatee, 100)); // Balance of 100 held on delegator account for delegating to the delegatee. assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &i), 100); } @@ -106,19 +106,17 @@ fn delegate_restrictions() { ExtBuilder::default().build_and_execute(|| { let delegatee_one = 200; let delegator_one = 210; - assert_ok!(DelegatedStaking::accept_delegations( - fund(&delegatee_one, 100), - &(delegatee_one + 1) - )); - assert_ok!(DelegatedStaking::delegate(fund(&delegator_one, 200), &delegatee_one, 100)); + fund(&delegatee_one, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegatee_one, &(delegatee_one + 1))); + fund(&delegator_one, 200); + assert_ok!(DelegatedStaking::delegate(&delegator_one, &delegatee_one, 100)); let delegatee_two = 300; let delegator_two = 310; - assert_ok!(DelegatedStaking::accept_delegations( - fund(&delegatee_two, 100), - &(delegatee_two + 1) - )); - assert_ok!(DelegatedStaking::delegate(fund(&delegator_two, 200), &delegatee_two, 100)); + fund(&delegatee_two, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegatee_two, &(delegatee_two + 1))); + fund(&delegator_two, 200); + assert_ok!(DelegatedStaking::delegate(&delegator_two, &delegatee_two, 100)); // delegatee one tries to delegate to delegatee 2 assert_noop!( @@ -164,13 +162,15 @@ mod integration { assert_eq!(Staking::status(&delegatee), Err(StakingError::::NotStash.into())); // set intention to become a delegatee - assert_ok!(DelegatedStaking::accept_delegations(fund(&delegatee, 100), &reward_acc)); + fund(&delegatee, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_acc)); assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); let mut delegated_balance: Balance = 0; // set some delegations for delegator in 200..250 { - assert_ok!(DelegatedStaking::delegate(fund(&delegator, 200), &delegatee, 100)); + fund(&delegator, 200); + assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); delegated_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), @@ -299,7 +299,8 @@ mod integration { ); // add new delegation that is not staked - assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &delegatee, 100)); + fund(&300, 1000); + assert_ok!(DelegatedStaking::delegate(&300, &delegatee, 100)); // verify unbonded balance assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); @@ -326,7 +327,8 @@ mod integration { // different reward account works assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); // add some delegations to it - assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &200, 100)); + fund(&300, 1000); + assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); // if delegatee calls Staking pallet directly with a different reward destination, it // fails. @@ -392,7 +394,8 @@ mod integration { assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); // delegation works - assert_ok!(DelegatedStaking::delegate(fund(&300, 1000), &200, 100)); + fund(&300, 1000); + assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); // delegatee blocks delegation assert_ok!(DelegatedStaking::block_delegations(&200)); @@ -445,7 +448,8 @@ mod integration { // to migrate, nominator needs to set an account as a proxy delegator where staked funds // will be moved and delegated back to this old nominator account. This should be funded // with at least ED. - let proxy_delegator = fund(&202, ExistentialDeposit::get()); + let proxy_delegator = 202; + fund(&proxy_delegator, ExistentialDeposit::get()); assert_ok!(DelegatedStaking::migrate_accept_delegations(&200, &proxy_delegator, &201)); assert!(DelegatedStaking::is_migrating(&200)); diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 55617fe32548..a6750a7c4c77 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -21,7 +21,9 @@ use frame_support::defensive; use sp_staking::{StakingAccount, StakingHoldProvider}; use sp_std::prelude::*; -use crate::{BalanceOf, Bonded, Config, Error, Ledger, Pallet, Payee, RewardDestination, StakingLedger}; +use crate::{ + BalanceOf, Bonded, Config, Error, Ledger, Pallet, Payee, RewardDestination, StakingLedger, +}; #[cfg(any(feature = "runtime-benchmarks", test))] use sp_runtime::traits::Zero; @@ -141,8 +143,7 @@ impl StakingLedger { return Err(Error::::NotStash) } - Pallet::::update_hold(&self.stash, self.total) - .map_err(|_| Error::::BadState)?; + Pallet::::update_hold(&self.stash, self.total).map_err(|_| Error::::BadState)?; Ledger::::insert( &self.controller().ok_or_else(|| { @@ -163,10 +164,7 @@ impl StakingLedger { return Err(Error::::AlreadyBonded); } - if Pallet::::restrict_reward_destination( - &self.stash, - payee.clone().from(&self.stash), - ) { + if Pallet::::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { return Err(Error::::RewardDestinationRestricted); } @@ -181,10 +179,7 @@ impl StakingLedger { return Err(Error::::NotStash); } - if Pallet::::restrict_reward_destination( - &self.stash, - payee.clone().from(&self.stash), - ) { + if Pallet::::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { return Err(Error::::RewardDestinationRestricted); } From b16ec42e4774a5d123d228b7215fe996280392b4 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 02:31:11 +0100 Subject: [PATCH 062/202] report slash --- substrate/frame/delegated-staking/src/lib.rs | 9 +++++++++ substrate/frame/staking/src/pallet/impls.rs | 8 ++++++-- substrate/frame/staking/src/slashing.rs | 12 ++++++++++-- substrate/primitives/staking/src/delegation.rs | 6 +++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 326519f8fccd..b668ee7732d9 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -511,6 +511,15 @@ impl StakingDelegationSupport for Pallet { fn is_delegatee(who: &Self::AccountId) -> bool { Self::is_delegatee(who) } + + fn report_slash(who: &Self::AccountId, slash: Self::Balance) { + >::mutate(who, |maybe_register| match maybe_register { + Some(register) => register.pending_slash.saturating_accrue(slash), + None => { + defensive!("should not be called on non-delegatee"); + }, + }); + } } /// StakingInterface implementation with delegation support. diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 16c83446fa62..637eb125fcd6 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1883,12 +1883,12 @@ impl StakingHoldProvider for NoDelegation { type AccountId = T::AccountId; fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { - defensive_assert!(true, "delegation update_hold should not be called for NoDelegation"); + defensive!("delegation update_hold should not be have been called for NoDelegation"); Err(Error::::NotEnoughFunds.into()) } fn release_all(_who: &Self::AccountId) { - defensive_assert!(true, "delegation release_all should not be called for NoDelegation"); + defensive!("delegation release_all should not have been called for NoDelegation"); } } @@ -1899,6 +1899,10 @@ impl StakingDelegationSupport for NoDelegation { fn is_delegatee(_who: &Self::AccountId) -> bool { false } + + fn report_slash(_who: &Self::AccountId, _slash: Self::Balance) { + defensive!("delegation report_slash should not be have been called for NoDelegation"); + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index 709fd1441ec3..2eeee0e8a3d6 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -64,7 +64,7 @@ use sp_runtime::{ traits::{Saturating, Zero}, DispatchResult, RuntimeDebug, }; -use sp_staking::{offence::DisableStrategy, EraIndex}; +use sp_staking::{delegation::StakingDelegationSupport, offence::DisableStrategy, EraIndex}; use sp_std::vec::Vec; /// The proportion of the slashing reward to be paid out on the first slashing detection. @@ -608,9 +608,17 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; + let lazy_slash = T::DelegationSupport::is_delegatee(stash); let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); - if !value.is_zero() { + if value.is_zero() { + // nothing to do + return; + } + if lazy_slash { + // If delegated staking, report slash and move on. + T::DelegationSupport::report_slash(stash, value); + } else { let (imbalance, missing) = T::Currency::slash(stash, value); slashed_imbalance.subsume(imbalance); diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 8d36941d2d1b..75503ca21d03 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -136,5 +136,9 @@ pub trait StakingDelegationSupport: StakingHoldProvider { false } - fn is_delegatee(_who: &Self::AccountId) -> bool; + /// Returns true if `who` accepts delegations for stake. + fn is_delegatee(who: &Self::AccountId) -> bool; + + /// Reports an ongoing slash to the delegatee account that would be applied lazily. + fn report_slash(who: &Self::AccountId, slash: Self::Balance); } From 360d5168a18e3102db07ee92a1d46877433a5be3 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 04:52:00 +0100 Subject: [PATCH 063/202] partial implement onslash, need to add more tests and integrations --- substrate/frame/delegated-staking/src/lib.rs | 55 +++++++++++++++---- substrate/frame/delegated-staking/src/mock.rs | 1 + substrate/frame/staking/src/pallet/impls.rs | 4 ++ substrate/primitives/staking/src/lib.rs | 8 +-- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index b668ee7732d9..66bf42c2f7fd 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -30,14 +30,17 @@ mod benchmarking; use frame_support::{ pallet_prelude::*, traits::{ - fungible::{hold::Mutate as FunHoldMutate, Inspect as FunInspect, Mutate as FunMutate}, - tokens::{Fortitude, Precision, Preservation}, - DefensiveOption, + fungible::{ + hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate}, + Balanced, Inspect as FunInspect, Mutate as FunMutate, + }, + tokens::{Fortitude, Precision, Preservation, fungible::Credit}, + DefensiveOption, Imbalance, OnUnbalanced, }, transactional, }; use pallet::*; -use sp_runtime::{traits::Zero, DispatchResult, RuntimeDebug, Saturating}; +use sp_runtime::{traits::Zero, DispatchResult, Perbill, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, EraIndex, Stake, StakerStatus, StakingHoldProvider, StakingInterface, @@ -60,7 +63,11 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type Currency: FunHoldMutate - + FunMutate; + + FunMutate + + FunHoldBalanced; + + /// Handler for the unbalanced reduction when slashing a delegator. + type OnSlash: OnUnbalanced>; /// Overarching hold reason. type RuntimeHoldReason: From; @@ -340,12 +347,36 @@ impl DelegationInterface for Pallet { } fn apply_slash( - _delegatee: &Self::AccountId, - _delegator: &Self::AccountId, - _value: Self::Balance, - _reporter: Option, + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, ) -> DispatchResult { - todo!() + let mut delegation_register = + >::get(delegatee).ok_or(Error::::NotDelegatee)?; + let (assigned_delegatee, delegate_balance) = + >::get(delegator).ok_or(Error::::NotDelegator)?; + + ensure!(&assigned_delegatee == delegatee, Error::::NotDelegatee); + ensure!(delegate_balance >= value, Error::::NotEnoughFunds); + + let (mut credit, _missing) = + T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); + let actual_slash = credit.peek(); + // remove the slashed amount + delegation_register.pending_slash.saturating_sub(actual_slash); + + if let Some(reporter) = maybe_reporter { + let reward_payout: BalanceOf = + T::CoreStaking::slash_reward_fraction() * actual_slash; + let (reporter_reward, rest) = credit.split(reward_payout); + credit = rest; + // fixme(ank4n): handle error + let _ = T::Currency::resolve(&reporter, reporter_reward); + } + + T::OnSlash::on_unbalanced(credit); + Ok(()) } /// Move funds from proxy delegator to actual delegator. @@ -678,6 +709,10 @@ impl StakingInterface for Pallet { T::CoreStaking::nominations(who) } + fn slash_reward_fraction() -> Perbill { + T::CoreStaking::slash_reward_fraction() + } + #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> sp_staking::Page { T::CoreStaking::max_exposure_page_size() diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 83666cdd1747..31c2f128bd6a 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -137,6 +137,7 @@ impl pallet_staking::Config for Runtime { impl delegated_staking::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; + type OnSlash = (); type RuntimeHoldReason = RuntimeHoldReason; type CoreStaking = Staking; } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 637eb125fcd6..0b2fb24b34e6 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1851,6 +1851,10 @@ impl StakingInterface for Pallet { T::MaxExposurePageSize::get() } } + + fn slash_reward_fraction() -> Perbill { + SlashRewardFraction::::get() + } } impl StakingHoldProvider for Pallet { diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index c32145f8c563..63b2784906cd 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -23,10 +23,7 @@ use crate::currency_to_vote::CurrencyToVote; use codec::{Decode, Encode, FullCodec, HasCompact, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime::{ - traits::{AtLeast32BitUnsigned, Zero}, - DispatchError, DispatchResult, RuntimeDebug, Saturating, -}; +use sp_runtime::{traits::{AtLeast32BitUnsigned, Zero}, DispatchError, DispatchResult, RuntimeDebug, Saturating, Perbill}; use sp_std::{collections::btree_map::BTreeMap, ops::Sub, vec, vec::Vec}; pub mod offence; @@ -285,6 +282,9 @@ pub trait StakingInterface { } } + /// Returns the fraction of the slash to be rewarded to reporter. + fn slash_reward_fraction() -> Perbill; + #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> Page; From 6cfa2525339b8dac208c8168a3a85a3a55f68996 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 05:11:35 +0100 Subject: [PATCH 064/202] doc --- substrate/frame/delegated-staking/src/lib.rs | 30 +++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 66bf42c2f7fd..1e3fe25f1cb1 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -18,6 +18,34 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] +//! An implementation of a delegation system for staking that can be utilised using +//! [`DelegationInterface`]. In future, if exposed via extrinsic, these primitives could also be +//! used by off-chain entities, smart contracts or by other parachains via xcm. +//! +//! Delegatee: Someone who accepts delegations. An account can set their intention to accept +//! delegations by calling [`DelegationInterface::accept_delegations`]. This account cannot have +//! another role in the staking system and once set as delegatee, can only stake with their +//! delegated balance, i.e. cannot use their own free balance to stake. They can also block new +//! delegations by calling [`DelegationInterface::block_delegations`] or remove themselves from +//! being a delegatee by calling [`DelegationInterface::kill_delegatee`] once all delegations to it +//! are removed. +//! +//! Delegatee is also responsible for managing reward distribution and slashes of delegators. +//! +//! Delegator: Someone who delegates their funds to a delegatee. A delegator can delegate their +//! funds to one and only one delegatee. They also can not be a nominator or validator. +//! +//! Reward payouts destination: Delegatees are restricted to have a reward payout destination that +//! is different from the delegatee account. This means, it cannot be auto-compounded and needs to +//! be staked again as a delegation. However, the reward payouts can then be distributed to +//! delegators by the delegatee. +//! +//! Any slashes to a delegatee are recorded in [`DelegationRegister`] of the Delegatee as a pending +//! slash. Since the actual amount is held in the delegator's account, this pallet does not know how +//! to apply slash. It is Delegatee's responsibility to apply slashes for each delegator, one at a +//! time. Staking pallet ensures the pending slash never exceeds staked amount and would freeze +//! further withdraws until pending slashes are applied. + #[cfg(test)] mod mock; @@ -34,7 +62,7 @@ use frame_support::{ hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate}, Balanced, Inspect as FunInspect, Mutate as FunMutate, }, - tokens::{Fortitude, Precision, Preservation, fungible::Credit}, + tokens::{fungible::Credit, Fortitude, Precision, Preservation}, DefensiveOption, Imbalance, OnUnbalanced, }, transactional, From 468f569bf1ab9427b1b745e52efe9e00d80841d9 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 05:11:52 +0100 Subject: [PATCH 065/202] fmt --- substrate/primitives/staking/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 63b2784906cd..25266877b45e 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -23,7 +23,10 @@ use crate::currency_to_vote::CurrencyToVote; use codec::{Decode, Encode, FullCodec, HasCompact, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime::{traits::{AtLeast32BitUnsigned, Zero}, DispatchError, DispatchResult, RuntimeDebug, Saturating, Perbill}; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Zero}, + DispatchError, DispatchResult, Perbill, RuntimeDebug, Saturating, +}; use sp_std::{collections::btree_map::BTreeMap, ops::Sub, vec, vec::Vec}; pub mod offence; From a151a0135a315effd245fed8d5e2c3f831a9c736 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 05:20:22 +0100 Subject: [PATCH 066/202] update register --- substrate/frame/delegated-staking/src/lib.rs | 3 ++- substrate/frame/delegated-staking/src/mock.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1e3fe25f1cb1..9ed9f6220a85 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -392,7 +392,8 @@ impl DelegationInterface for Pallet { T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); let actual_slash = credit.peek(); // remove the slashed amount - delegation_register.pending_slash.saturating_sub(actual_slash); + delegation_register.pending_slash.saturating_reduce(actual_slash); + >::insert(delegatee, delegation_register); if let Some(reporter) = maybe_reporter { let reward_payout: BalanceOf = diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 31c2f128bd6a..6c57adc6afef 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -129,6 +129,7 @@ impl pallet_staking::Config for Runtime { type TargetList = pallet_staking::UseValidatorsMap; type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; type MaxUnlockingChunks = ConstU32<32>; + type MaxControllersInDeprecationBatch = ConstU32<100>; type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); From 366809738bc70d59c5bcfab26e677ddba4828e10 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 14 Dec 2023 07:54:48 +0100 Subject: [PATCH 067/202] add dependencies to test staking --- Cargo.lock | 2 ++ substrate/frame/delegated-staking/src/lib.rs | 2 ++ .../nomination-pools/benchmarking/Cargo.toml | 3 +++ substrate/frame/nomination-pools/src/lib.rs | 3 ++- .../nomination-pools/test-staking/Cargo.toml | 1 + .../nomination-pools/test-staking/src/mock.rs | 18 ++++++++++++++---- 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6356a491be6..eae94190560e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10300,6 +10300,7 @@ dependencies = [ "frame-system", "pallet-bags-list", "pallet-balances", + "pallet-delegated-staking", "pallet-nomination-pools", "pallet-staking", "pallet-staking-reward-curve", @@ -10349,6 +10350,7 @@ dependencies = [ "log", "pallet-bags-list", "pallet-balances", + "pallet-delegated-staking", "pallet-nomination-pools", "pallet-staking", "pallet-staking-reward-curve", diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 9ed9f6220a85..b58058566c47 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -52,6 +52,8 @@ mod mock; #[cfg(test)] mod tests; +pub use pallet::*; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; diff --git a/substrate/frame/nomination-pools/benchmarking/Cargo.toml b/substrate/frame/nomination-pools/benchmarking/Cargo.toml index 8a4ee07dd744..387c65600bed 100644 --- a/substrate/frame/nomination-pools/benchmarking/Cargo.toml +++ b/substrate/frame/nomination-pools/benchmarking/Cargo.toml @@ -27,6 +27,7 @@ frame-support = { path = "../../support", default-features = false } frame-system = { path = "../../system", default-features = false } pallet-bags-list = { path = "../../bags-list", default-features = false } pallet-staking = { path = "../../staking", default-features = false } +pallet-delegated-staking = { path = "../../delegated-staking", default-features = false } pallet-nomination-pools = { path = "..", default-features = false } # Substrate Primitives @@ -55,6 +56,7 @@ std = [ "pallet-balances/std", "pallet-nomination-pools/std", "pallet-staking/std", + "pallet-delegated-staking/std", "pallet-timestamp/std", "scale-info/std", "sp-core/std", @@ -74,6 +76,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", "pallet-staking/runtime-benchmarks", + "pallet-delegated-staking/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index c3fd6a98e884..981ea8991d30 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1591,6 +1591,7 @@ pub mod pallet { use frame_support::traits::StorageVersion; use frame_system::{ensure_signed, pallet_prelude::*}; use sp_runtime::Perbill; + use sp_staking::delegation::DelegationInterface; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(8); @@ -1658,7 +1659,7 @@ pub mod pallet { type U256ToBalance: Convert>; /// The interface for nominating. - type Staking: StakingInterface, AccountId = Self::AccountId>; + type Staking: StakingInterface, AccountId = Self::AccountId> + DelegationInterface, AccountId = Self::AccountId>; /// The amount of eras a `SubPools::with_era` pool can exist before it gets merged into the /// `SubPools::no_era` pool. In other words, this is the amount of eras a member will be diff --git a/substrate/frame/nomination-pools/test-staking/Cargo.toml b/substrate/frame/nomination-pools/test-staking/Cargo.toml index 845535ae04f5..0302fe969c37 100644 --- a/substrate/frame/nomination-pools/test-staking/Cargo.toml +++ b/substrate/frame/nomination-pools/test-staking/Cargo.toml @@ -32,6 +32,7 @@ frame-election-provider-support = { path = "../../election-provider-support" } pallet-timestamp = { path = "../../timestamp" } pallet-balances = { path = "../../balances" } pallet-staking = { path = "../../staking" } +pallet-delegated-staking = { path = "../../delegated-staking" } pallet-bags-list = { path = "../../bags-list" } pallet-staking-reward-curve = { path = "../../staking/reward-curve" } pallet-nomination-pools = { path = ".." } diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 491cd6191619..f5a8fa83ae4e 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -88,9 +88,9 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; - type RuntimeHoldReason = (); - type RuntimeFreezeReason = (); - type MaxHolds = (); + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxHolds = ConstU32<1>; } pallet_staking_reward_curve::build! { @@ -112,6 +112,7 @@ parameter_types! { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; + type DelegationSupport = DelegatedStaking; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); @@ -168,6 +169,14 @@ impl Convert for U256ToBalance { } } +impl pallet_delegated_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type OnSlash = (); + type RuntimeHoldReason = RuntimeHoldReason; + type CoreStaking = Staking; +} + parameter_types! { pub const PostUnbondingPoolsWindow: u32 = 10; pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -181,7 +190,7 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type Staking = Staking; + type Staking = DelegatedStaking; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = ConstU32<8>; @@ -199,6 +208,7 @@ frame_support::construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Staking: pallet_staking::{Pallet, Call, Config, Storage, Event}, VoterList: pallet_bags_list::::{Pallet, Call, Storage, Event}, + DelegatedStaking: pallet_delegated_staking::{Pallet, Storage, Event, HoldReason}, Pools: pallet_nomination_pools::{Pallet, Call, Storage, Event, FreezeReason}, } ); From 27e59402f60c312405a1815095b90a2d327e4f39 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 15 Dec 2023 00:20:39 +0100 Subject: [PATCH 068/202] refactor from feedbacks --- substrate/frame/delegated-staking/src/lib.rs | 18 +++++++++--------- substrate/frame/delegated-staking/src/mock.rs | 2 +- substrate/frame/delegated-staking/src/tests.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 1 - substrate/frame/staking/src/slashing.rs | 3 ++- substrate/primitives/staking/src/delegation.rs | 4 ++-- substrate/primitives/staking/src/lib.rs | 1 + 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index b58058566c47..e5edbd96e318 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -40,7 +40,7 @@ //! be staked again as a delegation. However, the reward payouts can then be distributed to //! delegators by the delegatee. //! -//! Any slashes to a delegatee are recorded in [`DelegationRegister`] of the Delegatee as a pending +//! Any slashes to a delegatee are recorded in [`DelegationLedger`] of the Delegatee as a pending //! slash. Since the actual amount is held in the delegator's account, this pallet does not know how //! to apply slash. It is Delegatee's responsibility to apply slashes for each delegator, one at a //! time. Staking pallet ensures the pending slash never exceeds staked amount and would freeze @@ -176,7 +176,7 @@ pub mod pallet { /// Map of Delegatee to their Ledger. #[pallet::storage] pub(crate) type Delegatees = - CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationRegister, OptionQuery>; + CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; /// Map of Delegatee and its proxy delegator account while its actual delegators are migrating. /// @@ -194,13 +194,14 @@ pub mod pallet { /// among other things. #[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] -pub struct DelegationRegister { +pub struct DelegationLedger { /// Where the reward should be paid out. pub payee: T::AccountId, /// Sum of all delegated funds to this delegatee. #[codec(compact)] pub total_delegated: BalanceOf, /// Amount that is bonded and held. + // FIXME(ank4n) (can we remove it) #[codec(compact)] pub hold: BalanceOf, /// Slashes that are not yet applied. @@ -210,7 +211,7 @@ pub struct DelegationRegister { pub blocked: bool, } -impl DelegationRegister { +impl DelegationLedger { /// balance that can be staked. pub fn delegated_balance(&self) -> BalanceOf { // do not allow to stake more than unapplied slash @@ -259,7 +260,7 @@ impl DelegationInterface for Pallet { // already checked delegatees exist >::insert( who, - DelegationRegister { + DelegationLedger { payee: reward_destination.clone(), total_delegated: Zero::zero(), hold: Zero::zero(), @@ -318,7 +319,7 @@ impl DelegationInterface for Pallet { Self::accept_delegations(new_delegatee, payee)?; Self::delegate(proxy_delegator, new_delegatee, stake.total)?; - Self::update_bond(new_delegatee) + Self::bond_all(new_delegatee) } fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult { @@ -341,7 +342,7 @@ impl DelegationInterface for Pallet { todo!() } - fn update_bond(who: &Self::AccountId) -> DispatchResult { + fn bond_all(who: &Self::AccountId) -> DispatchResult { let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; let amount_to_bond = delegatee.unbonded_balance(); @@ -411,7 +412,6 @@ impl DelegationInterface for Pallet { } /// Move funds from proxy delegator to actual delegator. - // TODO: Keep track of proxy delegator and only allow movement from proxy -> new delegator #[transactional] fn migrate_delegator( delegatee: &Self::AccountId, @@ -525,7 +525,7 @@ impl StakingHoldProvider for Pallet { ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = DelegationRegister { hold: amount, ..delegation_register }; + let updated_register = DelegationLedger { hold: amount, ..delegation_register }; >::insert(who, updated_register); Ok(()) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 6c57adc6afef..cb207e2565dd 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -254,7 +254,7 @@ pub(crate) fn setup_delegation_stake( fund(delegator, amount_to_delegate + ExistentialDeposit::get()); assert_ok!(DelegatedStaking::delegate(delegator, &delegatee, amount_to_delegate)); - assert_ok!(DelegatedStaking::update_bond(&delegatee)); + assert_ok!(DelegatedStaking::bond_all(&delegatee)); } // sanity checks diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index b2f41e68a9c9..86e7de8f8c53 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -181,7 +181,7 @@ mod integration { // unbonded balance is the newly delegated 100 assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); - assert_ok!(DelegatedStaking::update_bond(&delegatee)); + assert_ok!(DelegatedStaking::bond_all(&delegatee)); // after bond, unbonded balance is 0 assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 0b2fb24b34e6..5cf47aaaf65b 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1737,7 +1737,6 @@ impl StakingInterface for Pallet { Self::chill(RawOrigin::Signed(ctrl).into()) } - // FIXME(ank4n): Refactor withdraw unbonded to take limit and remove partial_withdraw_unbonded fn withdraw_unbonded( who: Self::AccountId, num_slashing_spans: u32, diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index 2eeee0e8a3d6..ae4ea2bec909 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -619,7 +619,8 @@ pub fn do_slash( // If delegated staking, report slash and move on. T::DelegationSupport::report_slash(stash, value); } else { - let (imbalance, missing) = T::Currency::slash(stash, value); + let (imbalance, missing) = + T::Currency::slash(stash, value); slashed_imbalance.subsume(imbalance); if !missing.is_zero() { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 75503ca21d03..05ad7d70371c 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -79,8 +79,8 @@ pub trait DelegationInterface { /// This will only succeed if all delegations to this delegatee are withdrawn. fn kill_delegatee(delegatee: &Self::AccountId) -> DispatchResult; - /// Update bond whenever there is a new delegate funds that are not staked. - fn update_bond(delegatee: &Self::AccountId) -> DispatchResult; + /// Bond all fund that is delegated but not staked. + fn bond_all(delegatee: &Self::AccountId) -> DispatchResult; /// Request withdrawal of unbonded stake of `delegatee` belonging to the provided `delegator`. /// diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 25266877b45e..7561da4f2b10 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -437,6 +437,7 @@ pub trait StakingHoldProvider { type AccountId: Clone + sp_std::fmt::Debug; /// Update amount held for bonded stake. + /// FIXME(ank4n) remove this fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; /// Release all amount held for stake. From 199bb724f057c77013203257fca793059553fc45 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 15 Dec 2023 00:23:14 +0100 Subject: [PATCH 069/202] fmt --- substrate/frame/nomination-pools/src/lib.rs | 3 ++- substrate/frame/staking/src/slashing.rs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 981ea8991d30..5736700ca3e3 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1659,7 +1659,8 @@ pub mod pallet { type U256ToBalance: Convert>; /// The interface for nominating. - type Staking: StakingInterface, AccountId = Self::AccountId> + DelegationInterface, AccountId = Self::AccountId>; + type Staking: StakingInterface, AccountId = Self::AccountId> + + DelegationInterface, AccountId = Self::AccountId>; /// The amount of eras a `SubPools::with_era` pool can exist before it gets merged into the /// `SubPools::no_era` pool. In other words, this is the amount of eras a member will be diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index ae4ea2bec909..2eeee0e8a3d6 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -619,8 +619,7 @@ pub fn do_slash( // If delegated staking, report slash and move on. T::DelegationSupport::report_slash(stash, value); } else { - let (imbalance, missing) = - T::Currency::slash(stash, value); + let (imbalance, missing) = T::Currency::slash(stash, value); slashed_imbalance.subsume(imbalance); if !missing.is_zero() { From 6f4d150c7438ed889e6409ba6e39f938258a1e9f Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 15 Dec 2023 00:38:13 +0100 Subject: [PATCH 070/202] remove staking hold provider --- substrate/frame/delegated-staking/src/lib.rs | 54 ++++++++----------- substrate/frame/staking/src/ledger.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 50 +++++++---------- .../primitives/staking/src/delegation.rs | 20 ++++++- substrate/primitives/staking/src/lib.rs | 28 ++-------- 5 files changed, 65 insertions(+), 89 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index e5edbd96e318..0e46d838e8d9 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -69,11 +69,11 @@ use frame_support::{ }, transactional, }; -use pallet::*; + use sp_runtime::{traits::Zero, DispatchResult, Perbill, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, - EraIndex, Stake, StakerStatus, StakingHoldProvider, StakingInterface, + EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -102,8 +102,7 @@ pub mod pallet { type RuntimeHoldReason: From; /// Core staking implementation. - type CoreStaking: StakingInterface, AccountId = Self::AccountId> - + StakingHoldProvider, AccountId = Self::AccountId>; + type CoreStaking: StakingInterface, AccountId = Self::AccountId>; } #[pallet::error] @@ -512,35 +511,9 @@ impl DelegationInterface for Pallet { } } -impl StakingHoldProvider for Pallet { +impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; - - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); - - // delegation register should exist since `who` is a delegatee. - let delegation_register = - >::get(who).defensive_ok_or(Error::::BadState)?; - - ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); - ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = DelegationLedger { hold: amount, ..delegation_register }; - >::insert(who, updated_register); - - Ok(()) - } - - fn release_all(who: &Self::AccountId) { - if !Self::is_delegatee(who) { - T::CoreStaking::release_all(who); - } - - let _delegation_register = >::get(who); - todo!("handle kill delegatee") - } -} -impl StakingDelegationSupport for Pallet { fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { >::get(who) .map(|delegatee| delegatee.delegated_balance()) @@ -574,6 +547,21 @@ impl StakingDelegationSupport for Pallet { Self::is_delegatee(who) } + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + ensure!(Self::is_delegatee(who), Error::::NotSupported); + + // delegation register should exist since `who` is a delegatee. + let delegation_register = + >::get(who).defensive_ok_or(Error::::BadState)?; + + ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); + ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); + let updated_register = DelegationLedger { hold: amount, ..delegation_register }; + >::insert(who, updated_register); + + Ok(()) + } + fn report_slash(who: &Self::AccountId, slash: Self::Balance) { >::mutate(who, |maybe_register| match maybe_register { Some(register) => register.pending_slash.saturating_accrue(slash), @@ -744,6 +732,10 @@ impl StakingInterface for Pallet { T::CoreStaking::slash_reward_fraction() } + fn release_all(_who: &Self::AccountId) { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + } + #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> sp_staking::Page { T::CoreStaking::max_exposure_page_size() diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index a6750a7c4c77..c46b8d0d71a8 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -18,7 +18,7 @@ //! A Ledger implementation for stakers. use frame_support::defensive; -use sp_staking::{StakingAccount, StakingHoldProvider}; +use sp_staking::{StakingAccount, StakingInterface}; use sp_std::prelude::*; use crate::{ diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 5cf47aaaf65b..2ad3afef9e0f 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -44,7 +44,7 @@ use sp_staking::{ offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, Page, SessionIndex, Stake, StakingAccount::{self, Controller, Stash}, - StakingHoldProvider, StakingInterface, + StakingInterface, }; use sp_std::prelude::*; @@ -1114,6 +1114,18 @@ impl Pallet { false } + + pub(crate) fn update_hold( + who: &T::AccountId, + amount: BalanceOf, + ) -> sp_runtime::DispatchResult { + if T::DelegationSupport::is_delegatee(who) { + return T::DelegationSupport::update_hold(who, amount); + } + + T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); + Ok(()) + } } impl Pallet { @@ -1854,26 +1866,8 @@ impl StakingInterface for Pallet { fn slash_reward_fraction() -> Perbill { SlashRewardFraction::::get() } -} - -impl StakingHoldProvider for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> sp_runtime::DispatchResult { - if T::DelegationSupport::is_delegatee(who) { - return T::DelegationSupport::update_hold(who, amount); - } - - T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); - Ok(()) - } fn release_all(who: &Self::AccountId) { - if T::DelegationSupport::is_delegatee(who) { - return T::DelegationSupport::release_all(who); - } - T::Currency::remove_lock(crate::STAKING_ID, who) } } @@ -1881,27 +1875,19 @@ impl StakingHoldProvider for Pallet { /// Standard implementation of `StakingDelegationSupport` that supports only direct staking and no /// delegated staking. pub struct NoDelegation(PhantomData); -impl StakingHoldProvider for NoDelegation { +impl StakingDelegationSupport for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; - - fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { - defensive!("delegation update_hold should not be have been called for NoDelegation"); - Err(Error::::NotEnoughFunds.into()) - } - - fn release_all(_who: &Self::AccountId) { - defensive!("delegation release_all should not have been called for NoDelegation"); - } -} - -impl StakingDelegationSupport for NoDelegation { fn stakeable_balance(_who: &Self::AccountId) -> Self::Balance { BalanceOf::::zero() } fn is_delegatee(_who: &Self::AccountId) -> bool { false } + fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { + defensive!("delegation update_hold should not be have been called for NoDelegation"); + Err(Error::::NotEnoughFunds.into()) + } fn report_slash(_who: &Self::AccountId, _slash: Self::Balance) { defensive!("delegation report_slash should not be have been called for NoDelegation"); diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 05ad7d70371c..008850f7e820 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -15,7 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::StakingHoldProvider; use codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{DispatchResult, Saturating}; @@ -123,7 +122,21 @@ pub trait DelegationInterface { } /// Something that provides delegation support to core staking. -pub trait StakingDelegationSupport: StakingHoldProvider { +pub trait StakingDelegationSupport { + /// Balance type used by the staking system. + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + /// Balance of who which is available for stake. fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; @@ -139,6 +152,9 @@ pub trait StakingDelegationSupport: StakingHoldProvider { /// Returns true if `who` accepts delegations for stake. fn is_delegatee(who: &Self::AccountId) -> bool; + /// Update amount held for bonded stake. + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + /// Reports an ongoing slash to the delegatee account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 7561da4f2b10..dcf7fa41e288 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -288,6 +288,11 @@ pub trait StakingInterface { /// Returns the fraction of the slash to be rewarded to reporter. fn slash_reward_fraction() -> Perbill; + /// Release all funds bonded for stake. + /// + /// Unsafe, only used for migration of delegatee accounts. + fn release_all(who: &Self::AccountId); + #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> Page; @@ -420,27 +425,4 @@ pub struct PagedExposureMetadata { pub page_count: Page, } -/// Something that can hold and release funds for staking. -pub trait StakingHoldProvider { - /// Balance type used by the staking system. - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - - /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; - - /// Update amount held for bonded stake. - /// FIXME(ank4n) remove this - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; - - /// Release all amount held for stake. - fn release_all(who: &Self::AccountId); -} sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); From cb4d3ceb39194b78409770063b57258327f16a94 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 15 Dec 2023 23:18:49 +0100 Subject: [PATCH 071/202] some todos for NP integration --- substrate/frame/nomination-pools/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 5736700ca3e3..b0f85b1d6667 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1250,6 +1250,7 @@ impl BondedPool { ) -> Result, DispatchError> { // Cache the value let bonded_account = self.bonded_account(); + // TODO(ank4n) joining a pool: delegate funds to pool account.. T::Currency::transfer( who, &bonded_account, @@ -1264,6 +1265,7 @@ impl BondedPool { let points_issued = self.issue(amount); match ty { + // TODO(ank4n): When type create, also do accept delegation call.. BondType::Create => T::Staking::bond(&bonded_account, amount, &self.reward_account())?, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be From 2d61dd4400d5e5b49e7c36f8483370d93fafc81f Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 12 Feb 2024 14:54:57 +0100 Subject: [PATCH 072/202] fix compile after rebase with master --- substrate/frame/delegated-staking/src/mock.rs | 1 - substrate/frame/staking/src/lib.rs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index cb207e2565dd..ae062155dab0 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -74,7 +74,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = (); - type MaxHolds = ConstU32<1>; } pallet_staking_reward_curve::build! { diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 28c8c879b937..15ded13ef745 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -407,6 +407,21 @@ pub enum RewardDestination { None, } +impl RewardDestination { + fn from(self, stash: &AccountId) -> Option { + match self { + // FIXME(ank4n): Figure out later how to handle Controller + RewardDestination::Staked | RewardDestination::Stash => Some(stash.clone()), + RewardDestination::Account(a) => Some(a), + #[allow(deprecated)] + _ => { + defensive!("reward destination not set or set as deprecated controller"); + None + }, + } + } + +} /// Preference of what happens regarding validation. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default, MaxEncodedLen)] From 873b702ff67d888944878005121a46e0c54fd44d Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 15 Feb 2024 10:29:04 +0100 Subject: [PATCH 073/202] update dep --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28c66457eadf..82df11b91f83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9706,8 +9706,8 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "substrate-test-utils", ] From 1a6ce99ff5b3dc8af570aa6c9befc03965d82703 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 15 Feb 2024 10:43:16 +0100 Subject: [PATCH 074/202] fix conflicts --- .../nomination-pools/test-staking/src/mock.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 90676d566ea8..c01c91e5ad7d 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -199,25 +199,15 @@ impl pallet_nomination_pools::Config for Runtime { type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( -<<<<<<< HEAD - pub struct Runtime - { - System: frame_system::{Pallet, Call, Event}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Staking: pallet_staking::{Pallet, Call, Config, Storage, Event}, - VoterList: pallet_bags_list::::{Pallet, Call, Storage, Event}, - DelegatedStaking: pallet_delegated_staking::{Pallet, Storage, Event, HoldReason}, +pub enum Runtime { Pools: pallet_nomination_pools::{Pallet, Call, Storage, Event, FreezeReason}, -======= - pub enum Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, Staking: pallet_staking, VoterList: pallet_bags_list::, Pools: pallet_nomination_pools, ->>>>>>> master + DelegatedStaking: pallet_delegated_staking::{Pallet, Storage, Event, HoldReason}, } ); From b6709f921df229ec63bbb207105ec7062fc8899e Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 15 Feb 2024 10:43:51 +0100 Subject: [PATCH 075/202] new contruct runtime syntax --- substrate/frame/delegated-staking/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index ae062155dab0..d1296da22623 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -143,7 +143,7 @@ impl delegated_staking::Config for Runtime { } frame_support::construct_runtime!( - pub struct Runtime { + pub enum Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, From 05206ceaea1f7a653d139cdd24af4b3e3e1c9cee Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 15 Feb 2024 11:26:22 +0100 Subject: [PATCH 076/202] conflict compile fix --- substrate/frame/nomination-pools/test-staking/src/mock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index c01c91e5ad7d..7875ba41e3af 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -88,7 +88,8 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; - type MaxHolds = ConstU32<1>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; } pallet_staking_reward_curve::build! { @@ -200,7 +201,6 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Runtime { - Pools: pallet_nomination_pools::{Pallet, Call, Storage, Event, FreezeReason}, System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, From ee75c0570ee19f7b08aa98f66508fe612f5462e0 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 16 Feb 2024 01:44:21 +0100 Subject: [PATCH 077/202] rough np integration works --- substrate/frame/delegated-staking/src/lib.rs | 3 +- .../frame/delegated-staking/src/tests.rs | 30 ++-- substrate/frame/nomination-pools/src/lib.rs | 63 ++++--- .../nomination-pools/test-staking/src/lib.rs | 163 ++++++++++++++++++ substrate/frame/staking/src/pallet/mod.rs | 1 - .../primitives/staking/src/delegation.rs | 1 + 6 files changed, 210 insertions(+), 51 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 0e46d838e8d9..f06f22343c27 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -355,8 +355,8 @@ impl DelegationInterface for Pallet { #[transactional] fn withdraw( - delegator: &Self::AccountId, delegatee: &Self::AccountId, + delegator: &Self::AccountId, value: Self::Balance, num_slashing_spans: u32, ) -> DispatchResult { @@ -680,6 +680,7 @@ impl StakingInterface for Pallet { _stash: Self::AccountId, _num_slashing_spans: u32, ) -> Result { + // FIXME(ank4n): Support withdrawing to self account. defensive_assert!(false, "not supported for delegated impl of staking interface"); Err(Error::::NotSupported.into()) } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 86e7de8f8c53..6201d3f30ca2 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -210,10 +210,10 @@ mod integration { assert!(eq_stake(delegatee, total_staked, total_staked)); // Withdrawing without unbonding would fail. assert_noop!( - DelegatedStaking::withdraw(&300, &delegatee, 50, 0), + DelegatedStaking::withdraw(&delegatee, &300, 50, 0), Error::::WithdrawFailed ); - // assert_noop!(DelegatedStaking::withdraw(&200, &delegatee, 50, 0), + // assert_noop!(DelegatedStaking::withdraw(&delegatee, &200, 50, 0), // Error::::NotAllowed); active and total stake remains same assert!(eq_stake(delegatee, total_staked, total_staked)); @@ -232,7 +232,7 @@ mod integration { // nothing to withdraw at era 4 assert_noop!( - DelegatedStaking::withdraw(&305, &delegatee, 50, 0), + DelegatedStaking::withdraw(&delegatee, &305, 50, 0), Error::::WithdrawFailed ); @@ -244,43 +244,43 @@ mod integration { start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. assert_noop!( - DelegatedStaking::withdraw(&305, &delegatee, 51, 0), + DelegatedStaking::withdraw(&delegatee, &305, 51, 0), Error::::WithdrawFailed ); // less is possible - assert_ok!(DelegatedStaking::withdraw(&305, &delegatee, 30, 0)); - assert_ok!(DelegatedStaking::withdraw(&305, &delegatee, 20, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegatee, &305, 30, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegatee, &305, 20, 0)); // Lets go to future era where everything is unbonded. Withdrawable amount: 100 + 200 start_era(7); // 305 has no more amount delegated so it cannot withdraw. assert_noop!( - DelegatedStaking::withdraw(&305, &delegatee, 5, 0), + DelegatedStaking::withdraw(&delegatee, &305, 5, 0), Error::::NotDelegator ); // 309 is an active delegator but has total delegation of 90, so it cannot withdraw more // than that. assert_noop!( - DelegatedStaking::withdraw(&309, &delegatee, 91, 0), + DelegatedStaking::withdraw(&delegatee, &309, 91, 0), Error::::NotEnoughFunds ); // 310 cannot withdraw more than delegated funds. assert_noop!( - DelegatedStaking::withdraw(&310, &delegatee, 101, 0), + DelegatedStaking::withdraw(&delegatee, &310, 101, 0), Error::::NotEnoughFunds ); // but can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::withdraw(&310, &delegatee, 100, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegatee, &310, 100, 0)); // 320 can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::withdraw(&320, &delegatee, 200, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegatee, &320, 200, 0)); // cannot withdraw anything more.. assert_noop!( - DelegatedStaking::withdraw(&301, &delegatee, 1, 0), + DelegatedStaking::withdraw(&delegatee, &301, 1, 0), Error::::WithdrawFailed ); assert_noop!( - DelegatedStaking::withdraw(&350, &delegatee, 1, 0), + DelegatedStaking::withdraw(&delegatee, &350, 1, 0), Error::::WithdrawFailed ); }); @@ -294,7 +294,7 @@ mod integration { // verify withdraw not possible yet assert_noop!( - DelegatedStaking::withdraw(&300, &delegatee, 100, 0), + DelegatedStaking::withdraw(&delegatee, &300, 100, 0), Error::::WithdrawFailed ); @@ -306,7 +306,7 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); // withdraw works now without unbonding - assert_ok!(DelegatedStaking::withdraw(&300, &delegatee, 100, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegatee, &300, 100, 0)); assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); }); } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 0cbe26bc30a0..5182f69c32cd 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,7 +373,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{EraIndex, StakingInterface}; +use sp_staking::{EraIndex, StakingInterface, delegation::DelegationInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -1259,7 +1259,7 @@ impl BondedPool { T::Currency::transfer( who, &bonded_account, - amount, + T::Currency::minimum_balance(), match ty { BondType::Create => Preservation::Expendable, BondType::Later => Preservation::Preserve, @@ -1271,11 +1271,19 @@ impl BondedPool { match ty { // TODO(ank4n): When type create, also do accept delegation call.. - BondType::Create => T::Staking::bond(&bonded_account, amount, &self.reward_account())?, + BondType::Create => { + T::Staking::accept_delegations(&bonded_account, &self.reward_account())?; + T::Staking::delegate(&who, &bonded_account, amount)?; + T::Staking::bond(&bonded_account, amount, &self.reward_account())? + }, + // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. - BondType::Later => T::Staking::bond_extra(&bonded_account, amount)?, + BondType::Later => { + T::Staking::delegate(&who, &bonded_account, amount)?; + T::Staking::bond_extra(&bonded_account, amount)? + }, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); @@ -1577,7 +1585,6 @@ pub mod pallet { use frame_support::traits::StorageVersion; use frame_system::{ensure_signed, pallet_prelude::*}; use sp_runtime::Perbill; - use sp_staking::delegation::DelegationInterface; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(8); @@ -2256,18 +2263,6 @@ pub mod pallet { let withdrawn_points = member.withdraw_unlocked(current_era); ensure!(!withdrawn_points.is_empty(), Error::::CannotWithdrawAny); - // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the - // `transferrable_balance` is correct. - let stash_killed = - T::Staking::withdraw_unbonded(bonded_pool.bonded_account(), num_slashing_spans)?; - - // defensive-only: the depositor puts enough funds into the stash so that it will only - // be destroyed when they are leaving. - ensure!( - !stash_killed || caller == bonded_pool.roles.depositor, - Error::::Defensive(DefensiveError::BondedStashKilledPrematurely) - ); - let mut sum_unlocked_points: BalanceOf = Zero::zero(); let balance_to_unbond = withdrawn_points .iter() @@ -2284,23 +2279,23 @@ pub mod pallet { // era-less pool. accumulator.saturating_add(sub_pools.no_era.dissolve(*unlocked_points)) } - }) - // A call to this transaction may cause the pool's stash to get dusted. If this - // happens before the last member has withdrawn, then all subsequent withdraws will - // be 0. However the unbond pools do no get updated to reflect this. In the - // aforementioned scenario, this check ensures we don't try to withdraw funds that - // don't exist. This check is also defensive in cases where the unbond pool does not - // update its balance (e.g. a bug in the slashing hook.) We gracefully proceed in - // order to ensure members can leave the pool and it can be destroyed. - .min(bonded_pool.transferable_balance()); - - T::Currency::transfer( - &bonded_pool.bonded_account(), - &member_account, - balance_to_unbond, - Preservation::Expendable, - ) - .defensive()?; + }); + // fixme(ank4n): Transfer whatever is minimum transferable. + // Withdraw upto limit and return the amount withdrawn and whether stash killed. + + // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the + // `transferrable_balance` is correct. + // fixme(ank4n): Handle result. + let _withdraw_result = + T::Staking::withdraw(&bonded_pool.bonded_account(), &member_account, balance_to_unbond, num_slashing_spans)?; + + // defensive-only: the depositor puts enough funds into the stash so that it will only + // be destroyed when they are leaving. + // ensure!( + // !stash_killed || caller == bonded_pool.roles.depositor, + // Error::::Defensive(DefensiveError::BondedStashKilledPrematurely) + // ); + Self::deposit_event(Event::::Withdrawn { member: member_account.clone(), diff --git a/substrate/frame/nomination-pools/test-staking/src/lib.rs b/substrate/frame/nomination-pools/test-staking/src/lib.rs index 865b7a71e688..e960d027f3e0 100644 --- a/substrate/frame/nomination-pools/test-staking/src/lib.rs +++ b/substrate/frame/nomination-pools/test-staking/src/lib.rs @@ -191,6 +191,169 @@ fn pool_lifecycle_e2e() { }) } +#[test] +fn pool_migration_to_delegation_e2e() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::minimum_balance(), 5); + assert_eq!(Staking::current_era(), None); + + // create the pool, we know this has id 1. + assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); + assert_eq!(LastPoolId::::get(), 1); + + // have the pool nominate. + assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Created { depositor: 10, pool_id: 1 }, + PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, + ] + ); + + // have two members join + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, + PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true }, + ] + ); + + // pool goes into destroying + assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying)); + + // depositor cannot unbond yet. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 50), + PoolsError::::MinimumBondNotMet, + ); + + // now the members want to unbond. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); + + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_eras.len(), 1); + assert_eq!(PoolMembers::::get(20).unwrap().points, 0); + assert_eq!(PoolMembers::::get(21).unwrap().unbonding_eras.len(), 1); + assert_eq!(PoolMembers::::get(21).unwrap().points, 0); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, + PoolsEvent::Unbonded { member: 20, pool_id: 1, points: 10, balance: 10, era: 3 }, + PoolsEvent::Unbonded { member: 21, pool_id: 1, points: 10, balance: 10, era: 3 }, + ] + ); + + // depositor cannot still unbond + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 50), + PoolsError::::MinimumBondNotMet, + ); + + for e in 1..BondingDuration::get() { + CurrentEra::::set(Some(e)); + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0), + PoolsError::::CannotWithdrawAny + ); + } + + // members are now unlocked. + CurrentEra::::set(Some(BondingDuration::get())); + + // depositor cannot still unbond + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 50), + PoolsError::::MinimumBondNotMet, + ); + + // but members can now withdraw. + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); + assert!(PoolMembers::::get(20).is_none()); + assert!(PoolMembers::::get(21).is_none()); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }, + ] + ); + + // as soon as all members have left, the depositor can try to unbond, but since the + // min-nominator intention is set, they must chill first. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 50), + pallet_staking::Error::::InsufficientBond + ); + + assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Chilled { stash: POOL1_BONDED }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 }, + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 50, balance: 50, era: 6 }] + ); + + // waiting another bonding duration: + CurrentEra::::set(Some(BondingDuration::get() * 2)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1)); + + // pools is fully destroyed now. + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::Destroyed { pool_id: 1 } + ] + ); + }) +} + #[test] fn pool_slash_e2e() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 50faa5b963f9..dec406e4e2d0 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -948,7 +948,6 @@ pub mod pallet { } frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; - let stash_balance = Self::stakeable_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 008850f7e820..da2ec2742750 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -79,6 +79,7 @@ pub trait DelegationInterface { fn kill_delegatee(delegatee: &Self::AccountId) -> DispatchResult; /// Bond all fund that is delegated but not staked. + /// FIXME(ank4n): Should not be allowed as withdrawn funds would get restaked. fn bond_all(delegatee: &Self::AccountId) -> DispatchResult; /// Request withdrawal of unbonded stake of `delegatee` belonging to the provided `delegator`. From 5482c9685f33fa1686af663dd7a13ae0e32c457e Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 06:55:15 +0100 Subject: [PATCH 078/202] restore doc --- substrate/frame/staking/src/ledger.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 7543f8d1a70f..eed459500a64 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -16,6 +16,20 @@ // limitations under the License. //! A Ledger implementation for stakers. +//! +//! A [`StakingLedger`] encapsulates all the state and logic related to the stake of bonded +//! stakers, namely, it handles the following storage items: +//! * [`Bonded`]: mutates and reads the state of the controller <> stash bond map (to be deprecated +//! soon); +//! * [`Ledger`]: mutates and reads the state of all the stakers. The [`Ledger`] storage item stores +//! instances of [`StakingLedger`] keyed by the staker's controller account and should be mutated +//! and read through the [`StakingLedger`] API; +//! * [`Payee`]: mutates and reads the reward destination preferences for a bonded stash. +//! * Staking locks: mutates the locks for staking. +//! +//! NOTE: All the storage operations related to the staking ledger (both reads and writes) *MUST* be +//! performed through the methods exposed by the [`StakingLedger`] implementation in order to ensure +//! state consistency. use frame_support::defensive; use sp_staking::{StakingAccount, StakingInterface}; From 7d901572d2b98ec20a06a1057f786c1ef7164525 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 07:24:42 +0100 Subject: [PATCH 079/202] rename delegatee to delegate --- substrate/frame/delegated-staking/src/lib.rs | 284 +++++++++--------- substrate/frame/delegated-staking/src/mock.rs | 14 +- .../frame/delegated-staking/src/tests.rs | 184 ++++++------ substrate/frame/staking/src/pallet/impls.rs | 8 +- substrate/frame/staking/src/slashing.rs | 2 +- .../primitives/staking/src/delegation.rs | 49 +-- substrate/primitives/staking/src/lib.rs | 2 +- 7 files changed, 268 insertions(+), 275 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index f06f22343c27..8e7fefae24bc 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -22,27 +22,26 @@ //! [`DelegationInterface`]. In future, if exposed via extrinsic, these primitives could also be //! used by off-chain entities, smart contracts or by other parachains via xcm. //! -//! Delegatee: Someone who accepts delegations. An account can set their intention to accept +//! Delegate: Someone who accepts delegations. An account can set their intention to accept //! delegations by calling [`DelegationInterface::accept_delegations`]. This account cannot have -//! another role in the staking system and once set as delegatee, can only stake with their +//! another role in the staking system and once set as `delegate`, can only stake with their //! delegated balance, i.e. cannot use their own free balance to stake. They can also block new //! delegations by calling [`DelegationInterface::block_delegations`] or remove themselves from -//! being a delegatee by calling [`DelegationInterface::kill_delegatee`] once all delegations to it +//! being a `delegate` by calling [`DelegationInterface::kill_delegate`] once all delegations to it //! are removed. //! -//! Delegatee is also responsible for managing reward distribution and slashes of delegators. +//! Delegate is also responsible for managing reward distribution and slashes of delegators. //! -//! Delegator: Someone who delegates their funds to a delegatee. A delegator can delegate their -//! funds to one and only one delegatee. They also can not be a nominator or validator. +//! Delegator: Someone who delegates their funds to a `delegate`. A delegator can delegate their +//! funds to one and only one `delegate`. They also can not be a nominator or validator. //! -//! Reward payouts destination: Delegatees are restricted to have a reward payout destination that -//! is different from the delegatee account. This means, it cannot be auto-compounded and needs to -//! be staked again as a delegation. However, the reward payouts can then be distributed to -//! delegators by the delegatee. +//! Reward payouts destination: Rewards cannot be paid out to `delegate` account since these funds +//! are not directly exposed. This implies, rewards cannot be auto-compounded and needs to be staked +//! again after distributing it to delegators. //! -//! Any slashes to a delegatee are recorded in [`DelegationLedger`] of the Delegatee as a pending -//! slash. Since the actual amount is held in the delegator's account, this pallet does not know how -//! to apply slash. It is Delegatee's responsibility to apply slashes for each delegator, one at a +//! Any slashes to a `delegate` are posted in its [`DelegationLedger`] as a pending slash. Since the +//! actual amount is held in the multiple `delegator` accounts, this pallet has no way to know how +//! to apply slash. It is `delegate`'s responsibility to apply slashes for each delegator, one at a //! time. Staking pallet ensures the pending slash never exceeds staked amount and would freeze //! further withdraws until pending slashes are applied. @@ -109,34 +108,34 @@ pub mod pallet { pub enum Error { /// The account cannot perform this operation. NotAllowed, - /// An existing staker cannot become a delegatee. + /// An existing staker cannot become a `delegate`. AlreadyStaker, - /// Reward Destination cannot be delegatee account. + /// Reward Destination cannot be `delegate` account. InvalidRewardDestination, /// Delegation conditions are not met. /// /// Possible issues are /// 1) Account does not accept or has blocked delegation. /// 2) Cannot delegate to self, - /// 3) Cannot delegate to multiple Delegatees, + /// 3) Cannot delegate to multiple delegates, InvalidDelegation, /// The account does not have enough funds to perform the operation. NotEnoughFunds, - /// Not an existing delegatee account. - NotDelegatee, + /// Not an existing `delegate` account. + NotDelegate, /// Not a Delegator account. NotDelegator, /// Some corruption in internal state. BadState, - /// Unapplied pending slash restricts operation on delegatee. + /// Unapplied pending slash restricts operation on `delegate`. UnappliedSlash, /// Failed to withdraw amount from Core Staking Ledger. WithdrawFailed, /// This operation is not supported with Delegation Staking. NotSupported, - /// This delegatee is not set as a migrating account. + /// This `delegate` is not set as a migrating account. NotMigrating, - /// Delegatee no longer accepting new delegations. + /// Delegate no longer accepting new delegations. DelegationsBlocked, } @@ -160,43 +159,43 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - Delegated { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, - Withdrawn { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Delegated { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Withdrawn { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, } - /// Map of Delegators to their delegation, i.e. (delegatee, delegation_amount). + /// Map of Delegators to their delegation, i.e. (delegate, delegation_amount). /// - /// Note: We are not using a double map with delegator and delegatee account as keys since we + /// Note: We are not using a double map with delegator and `delegate` account as keys since we /// want to restrict delegators to delegate only to one account. #[pallet::storage] pub(crate) type Delegators = CountedStorageMap<_, Twox64Concat, T::AccountId, (T::AccountId, BalanceOf), OptionQuery>; - /// Map of Delegatee to their Ledger. + /// Map of Delegate to their Ledger. #[pallet::storage] - pub(crate) type Delegatees = + pub(crate) type Delegates = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; - /// Map of Delegatee and its proxy delegator account while its actual delegators are migrating. + /// Map of Delegate and its proxy delegator account while its actual delegators are migrating. /// - /// Helps ensure correctness of ongoing migration of a direct nominator to a delegatee. If a - /// delegatee does not exist, it implies it is not going through migration. + /// Helps ensure correctness of ongoing migration of a direct nominator to a `delegate`. If a + /// `delegate` does not exist, it implies it is not going through migration. #[pallet::storage] - pub(crate) type DelegateeMigration = + pub(crate) type DelegateMigration = CountedStorageMap<_, Twox64Concat, T::AccountId, T::AccountId, OptionQuery>; } -/// Register of all delegations to a `Delegatee`. +/// Register of all delegations to a `Delegate`. /// -/// This keeps track of the active balance of the delegatee that is made up from the funds that are -/// currently delegated to this delegatee. It also tracks the pending slashes yet to be applied +/// This keeps track of the active balance of the `delegate` that is made up from the funds that are +/// currently delegated to this `delegate`. It also tracks the pending slashes yet to be applied /// among other things. #[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct DelegationLedger { /// Where the reward should be paid out. pub payee: T::AccountId, - /// Sum of all delegated funds to this delegatee. + /// Sum of all delegated funds to this `delegate`. #[codec(compact)] pub total_delegated: BalanceOf, /// Amount that is bonded and held. @@ -206,7 +205,7 @@ pub struct DelegationLedger { /// Slashes that are not yet applied. #[codec(compact)] pub pending_slash: BalanceOf, - /// Whether this delegatee is blocked from receiving new delegations. + /// Whether this `delegate` is blocked from receiving new delegations. pub blocked: bool, } @@ -228,12 +227,12 @@ impl DelegationInterface for Pallet { type AccountId = T::AccountId; fn delegated_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) + >::get(who) .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) } fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) + >::get(who) .map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) } @@ -241,23 +240,23 @@ impl DelegationInterface for Pallet { who: &Self::AccountId, reward_destination: &Self::AccountId, ) -> DispatchResult { - // Existing delegatee cannot register again. - ensure!(!>::contains_key(who), Error::::NotAllowed); + // Existing `delegate` cannot register again. + ensure!(!>::contains_key(who), Error::::NotAllowed); - // A delegator cannot become a delegatee. + // A delegator cannot become a `delegate`. ensure!(!>::contains_key(who), Error::::NotAllowed); - // payee account cannot be same as delegatee + // payee account cannot be same as `delegate` ensure!(reward_destination != who, Error::::InvalidRewardDestination); // make sure they are not already a direct staker or they are migrating. ensure!( - T::CoreStaking::status(who).is_err() || >::contains_key(who), + T::CoreStaking::status(who).is_err() || >::contains_key(who), Error::::AlreadyStaker ); - // already checked delegatees exist - >::insert( + // already checked that `delegate` exist + >::insert( who, DelegationLedger { payee: reward_destination.clone(), @@ -272,13 +271,13 @@ impl DelegationInterface for Pallet { } /// Transfers funds from current staked account to `proxy_delegator`. Current staked account - /// becomes a delegatee with `proxy_delegator` delegating stakes to it. + /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. fn migrate_accept_delegations( - new_delegatee: &Self::AccountId, + new_delegate: &Self::AccountId, proxy_delegator: &Self::AccountId, payee: &Self::AccountId, ) -> DispatchResult { - ensure!(new_delegatee != proxy_delegator, Error::::InvalidDelegation); + ensure!(new_delegate != proxy_delegator, Error::::InvalidDelegation); // ensure proxy delegator has at least minimum balance to keep the account alive. ensure!( @@ -291,103 +290,97 @@ impl DelegationInterface for Pallet { ); // ensure staker is a nominator - let status = T::CoreStaking::status(new_delegatee)?; + let status = T::CoreStaking::status(new_delegate)?; match status { StakerStatus::Nominator(_) => (), _ => return Err(Error::::InvalidDelegation.into()), } - >::insert(&new_delegatee, &proxy_delegator); - let stake = T::CoreStaking::stake(new_delegatee)?; + >::insert(&new_delegate, &proxy_delegator); + let stake = T::CoreStaking::stake(new_delegate)?; // unlock funds from staker - T::CoreStaking::release_all(new_delegatee); + T::CoreStaking::release_all(new_delegate); // try transferring the staked amount. This should never fail but if it does, it indicates // bad state and we abort. - T::Currency::transfer( - new_delegatee, - proxy_delegator, - stake.total, - Preservation::Expendable, - ) - .map_err(|_| Error::::BadState)?; + T::Currency::transfer(new_delegate, proxy_delegator, stake.total, Preservation::Expendable) + .map_err(|_| Error::::BadState)?; // delegate from new delegator to staker. // todo(ank4n) : inline this fn and propagate payee to core staking.. - Self::accept_delegations(new_delegatee, payee)?; + Self::accept_delegations(new_delegate, payee)?; - Self::delegate(proxy_delegator, new_delegatee, stake.total)?; - Self::bond_all(new_delegatee) + Self::delegate(proxy_delegator, new_delegate, stake.total)?; + Self::bond_all(new_delegate) } - fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult { - let mut register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { + let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; register.blocked = true; - >::insert(delegatee, register); + >::insert(delegate, register); Ok(()) } - fn unblock_delegations(delegatee: &Self::AccountId) -> DispatchResult { - let mut register = >::get(delegatee).ok_or(Error::::NotDelegatee)?; + fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult { + let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; register.blocked = false; - >::insert(delegatee, register); + >::insert(delegate, register); Ok(()) } - fn kill_delegatee(_delegatee: &Self::AccountId) -> DispatchResult { + fn kill_delegate(_delegate: &Self::AccountId) -> DispatchResult { todo!() } fn bond_all(who: &Self::AccountId) -> DispatchResult { - let delegatee = >::get(who).ok_or(Error::::NotDelegatee)?; - let amount_to_bond = delegatee.unbonded_balance(); + let delegate = >::get(who).ok_or(Error::::NotDelegate)?; + let amount_to_bond = delegate.unbonded_balance(); match T::CoreStaking::stake(who) { // already bonded Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), // first bond - Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegatee.payee), + Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegate.payee), } } #[transactional] fn withdraw( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, num_slashing_spans: u32, ) -> DispatchResult { // check how much is already unbonded - let delegation_register = - >::get(delegatee).ok_or(Error::::NotDelegatee)?; + let delegation_register = >::get(delegate).ok_or(Error::::NotDelegate)?; let unbonded_balance = delegation_register.unbonded_balance(); if unbonded_balance < value { // fixme(ank4n) handle killing of stash let amount_to_withdraw = value.saturating_sub(unbonded_balance); let _stash_killed: bool = - T::CoreStaking::withdraw_exact(delegatee, amount_to_withdraw, num_slashing_spans) + T::CoreStaking::withdraw_exact(delegate, amount_to_withdraw, num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; } - Self::delegation_withdraw(delegator, delegatee, value) + Self::delegation_withdraw(delegator, delegate, value) } fn apply_slash( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option, ) -> DispatchResult { let mut delegation_register = - >::get(delegatee).ok_or(Error::::NotDelegatee)?; - let (assigned_delegatee, delegate_balance) = + >::get(delegate).ok_or(Error::::NotDelegate)?; + let (assigned_delegate, delegate_balance) = >::get(delegator).ok_or(Error::::NotDelegator)?; - ensure!(&assigned_delegatee == delegatee, Error::::NotDelegatee); + ensure!(&assigned_delegate == delegate, Error::::NotDelegate); ensure!(delegate_balance >= value, Error::::NotEnoughFunds); let (mut credit, _missing) = @@ -395,7 +388,7 @@ impl DelegationInterface for Pallet { let actual_slash = credit.peek(); // remove the slashed amount delegation_register.pending_slash.saturating_reduce(actual_slash); - >::insert(delegatee, delegation_register); + >::insert(delegate, delegation_register); if let Some(reporter) = maybe_reporter { let reward_payout: BalanceOf = @@ -413,21 +406,21 @@ impl DelegationInterface for Pallet { /// Move funds from proxy delegator to actual delegator. #[transactional] fn migrate_delegator( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, new_delegator: &Self::AccountId, value: Self::Balance, ) -> DispatchResult { ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - // make sure new delegator is not an existing delegator or a delegatee - ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); + // make sure new delegator is not an existing delegator or a delegate + ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); // ensure we are migrating let proxy_delegator = - >::get(delegatee).ok_or(Error::::NotMigrating)?; + >::get(delegate).ok_or(Error::::NotMigrating)?; // proxy delegator must exist - let (assigned_delegatee, delegate_balance) = + let (assigned_delegate, delegate_balance) = >::get(&proxy_delegator).ok_or(Error::::BadState)?; - ensure!(assigned_delegatee == *delegatee, Error::::BadState); + ensure!(assigned_delegate == *delegate, Error::::BadState); // make sure proxy delegator has enough balance to support this migration. ensure!(delegate_balance >= value, Error::::NotEnoughFunds); @@ -438,10 +431,10 @@ impl DelegationInterface for Pallet { // if all funds are migrated out of proxy delegator, clean up. if updated_delegate_balance == BalanceOf::::zero() { >::remove(&proxy_delegator); - >::remove(delegatee); + >::remove(delegate); } else { // else update proxy delegator - >::insert(&proxy_delegator, (delegatee, updated_delegate_balance)); + >::insert(&proxy_delegator, (delegate, updated_delegate_balance)); } let released = T::Currency::release( @@ -458,7 +451,7 @@ impl DelegationInterface for Pallet { .map_err(|_| Error::::BadState)?; // add the above removed delegation to `new_delegator`. - >::insert(new_delegator, (delegatee, value)); + >::insert(new_delegator, (delegate, value)); // hold the funds again in the new delegator account. T::Currency::hold(&HoldReason::Delegating.into(), &new_delegator, value)?; @@ -467,42 +460,41 @@ impl DelegationInterface for Pallet { fn delegate( delegator: &Self::AccountId, - delegatee: &Self::AccountId, + delegate: &Self::AccountId, value: Self::Balance, ) -> DispatchResult { let delegator_balance = T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); ensure!(delegator_balance >= value, Error::::NotEnoughFunds); - ensure!(delegatee != delegator, Error::::InvalidDelegation); + ensure!(delegate != delegator, Error::::InvalidDelegation); let mut delegation_register = - >::get(delegatee).ok_or(Error::::NotDelegatee)?; + >::get(delegate).ok_or(Error::::NotDelegate)?; ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); - // A delegatee cannot delegate. - if >::contains_key(delegator) { + // A delegate cannot delegate. + if >::contains_key(delegator) { return Err(Error::::InvalidDelegation.into()) } - let new_delegation_amount = if let Some((current_delegatee, current_delegation)) = - >::get(delegator) - { - ensure!(¤t_delegatee == delegatee, Error::::InvalidDelegation); - value.saturating_add(current_delegation) - } else { - value - }; + let new_delegation_amount = + if let Some((current_delegate, current_delegation)) = >::get(delegator) { + ensure!(¤t_delegate == delegate, Error::::InvalidDelegation); + value.saturating_add(current_delegation) + } else { + value + }; delegation_register.total_delegated.saturating_accrue(value); - >::insert(delegator, (delegatee, new_delegation_amount)); - >::insert(delegatee, delegation_register); + >::insert(delegator, (delegate, new_delegation_amount)); + >::insert(delegate, delegation_register); T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; Self::deposit_event(Event::::Delegated { - delegatee: delegatee.clone(), + delegate: delegate.clone(), delegator: delegator.clone(), amount: value, }); @@ -515,8 +507,8 @@ impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map(|delegatee| delegatee.delegated_balance()) + >::get(who) + .map(|delegate| delegate.delegated_balance()) .unwrap_or_default() } @@ -524,10 +516,10 @@ impl StakingDelegationSupport for Pallet { who: &Self::AccountId, reward_destination: Option, ) -> bool { - let maybe_register = >::get(who); + let maybe_register = >::get(who); if maybe_register.is_none() { - // no restrictions for non delegatees. + // no restrictions for non delegates. return false; } @@ -539,34 +531,34 @@ impl StakingDelegationSupport for Pallet { let register = maybe_register.expect("checked above; qed"); let reward_acc = reward_destination.expect("checked above; qed"); - // restrict if reward account is not what delegatee registered. + // restrict if reward account is not what delegate registered. register.payee != reward_acc } - fn is_delegatee(who: &Self::AccountId) -> bool { - Self::is_delegatee(who) + fn is_delegate(who: &Self::AccountId) -> bool { + Self::is_delegate(who) } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegate(who), Error::::NotSupported); - // delegation register should exist since `who` is a delegatee. + // delegation register should exist since `who` is a delegate. let delegation_register = - >::get(who).defensive_ok_or(Error::::BadState)?; + >::get(who).defensive_ok_or(Error::::BadState)?; ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); let updated_register = DelegationLedger { hold: amount, ..delegation_register }; - >::insert(who, updated_register); + >::insert(who, updated_register); Ok(()) } fn report_slash(who: &Self::AccountId, slash: Self::Balance) { - >::mutate(who, |maybe_register| match maybe_register { + >::mutate(who, |maybe_register| match maybe_register { Some(register) => register.pending_slash.saturating_accrue(slash), None => { - defensive!("should not be called on non-delegatee"); + defensive!("should not be called on non-delegate"); }, }); } @@ -574,8 +566,8 @@ impl StakingDelegationSupport for Pallet { /// StakingInterface implementation with delegation support. /// -/// Only supports Nominators via Delegated Bonds. It is possible for a nominator to migrate to a -/// Delegatee. +/// Only supports Nominators via Delegated Bonds. It is possible for a nominator to migrate and +/// become a `Delegate`. impl StakingInterface for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -605,12 +597,12 @@ impl StakingInterface for Pallet { } fn stake(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegate(who), Error::::NotSupported); return T::CoreStaking::stake(who); } fn total_stake(who: &Self::AccountId) -> Result { - if Self::is_delegatee(who) { + if Self::is_delegate(who) { return T::CoreStaking::total_stake(who); } @@ -632,7 +624,7 @@ impl StakingInterface for Pallet { } fn fully_unbond(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegate(who), Error::::NotSupported); return T::CoreStaking::fully_unbond(who); } @@ -642,8 +634,8 @@ impl StakingInterface for Pallet { payee: &Self::AccountId, ) -> DispatchResult { // ensure who is not already staked - ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegatee); - let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; + ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegate); + let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; ensure!(delegation_register.unbonded_balance() >= value, Error::::NotEnoughFunds); ensure!(delegation_register.payee == *payee, Error::::InvalidRewardDestination); @@ -652,30 +644,30 @@ impl StakingInterface for Pallet { } fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegate(who), Error::::NotSupported); return T::CoreStaking::nominate(who, validators); } fn chill(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegate(who), Error::::NotSupported); return T::CoreStaking::chill(who); } fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { - let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; + let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; ensure!(delegation_register.unbonded_balance() >= extra, Error::::NotEnoughFunds); T::CoreStaking::bond_extra(who, extra) } fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { - let delegation_register = >::get(stash).ok_or(Error::::NotDelegatee)?; + let delegation_register = >::get(stash).ok_or(Error::::NotDelegate)?; ensure!(delegation_register.hold >= value, Error::::NotEnoughFunds); T::CoreStaking::unbond(stash, value) } - /// Not supported, call [`Delegatee::withdraw`] + /// Not supported, call [`Delegate::withdraw`] fn withdraw_unbonded( _stash: Self::AccountId, _num_slashing_spans: u32, @@ -685,7 +677,7 @@ impl StakingInterface for Pallet { Err(Error::::NotSupported.into()) } - /// Not supported, call [`Delegatee::withdraw`] + /// Not supported, call [`Delegate::withdraw`] fn withdraw_exact( _stash: &Self::AccountId, _amount: Self::Balance, @@ -716,7 +708,7 @@ impl StakingInterface for Pallet { } fn status(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegate(who), Error::::NotSupported); T::CoreStaking::status(who) } @@ -757,34 +749,34 @@ impl StakingInterface for Pallet { } } impl Pallet { - fn is_delegatee(who: &T::AccountId) -> bool { - >::contains_key(who) + fn is_delegate(who: &T::AccountId) -> bool { + >::contains_key(who) } fn is_delegator(who: &T::AccountId) -> bool { >::contains_key(who) } - fn is_migrating(delegatee: &T::AccountId) -> bool { - >::contains_key(delegatee) + fn is_migrating(delegate: &T::AccountId) -> bool { + >::contains_key(delegate) } fn delegation_withdraw( delegator: &T::AccountId, - delegatee: &T::AccountId, + delegate: &T::AccountId, value: BalanceOf, ) -> DispatchResult { let mut delegation_register = - >::get(delegatee).ok_or(Error::::NotDelegatee)?; + >::get(delegate).ok_or(Error::::NotDelegate)?; ensure!(delegation_register.unbonded_balance() >= value, Error::::BadState); delegation_register.total_delegated.saturating_reduce(value); - >::insert(delegatee, delegation_register); + >::insert(delegate, delegation_register); - let (assigned_delegatee, delegate_balance) = + let (assigned_delegate, delegate_balance) = >::get(delegator).ok_or(Error::::NotDelegator)?; - // delegator should already be delegating to delegatee - ensure!(&assigned_delegatee == delegatee, Error::::NotDelegatee); + // delegator should already be delegating to `delegate` + ensure!(&assigned_delegate == delegate, Error::::NotDelegate); ensure!(delegate_balance >= value, Error::::NotEnoughFunds); let updated_delegate_balance = delegate_balance.saturating_sub(value); @@ -792,7 +784,7 @@ impl Pallet { if updated_delegate_balance == BalanceOf::::zero() { >::remove(delegator); } else { - >::insert(delegator, (delegatee, updated_delegate_balance)); + >::insert(delegator, (delegate, updated_delegate_balance)); } let released = T::Currency::release( @@ -804,7 +796,7 @@ impl Pallet { defensive_assert!(released == value, "hold should have been released fully"); Self::deposit_event(Event::::Withdrawn { - delegatee: delegatee.clone(), + delegate: delegate.clone(), delegator: delegator.clone(), amount: value, }); diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index d1296da22623..82c31c66038d 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -238,27 +238,27 @@ pub(crate) fn fund(who: &AccountId, amount: Balance) { /// `delegate_amount` is incremented by the amount `increment` starting with `base_delegate_amount` /// from lower index to higher index of delegators. pub(crate) fn setup_delegation_stake( - delegatee: AccountId, + delegate: AccountId, reward_acc: AccountId, delegators: Vec, base_delegate_amount: Balance, increment: Balance, ) -> Balance { - fund(&delegatee, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_acc)); + fund(&delegate, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_acc)); let mut delegated_amount: Balance = 0; for (index, delegator) in delegators.iter().enumerate() { let amount_to_delegate = base_delegate_amount + increment * index as Balance; delegated_amount += amount_to_delegate; fund(delegator, amount_to_delegate + ExistentialDeposit::get()); - assert_ok!(DelegatedStaking::delegate(delegator, &delegatee, amount_to_delegate)); - assert_ok!(DelegatedStaking::bond_all(&delegatee)); + assert_ok!(DelegatedStaking::delegate(delegator, &delegate, amount_to_delegate)); + assert_ok!(DelegatedStaking::bond_all(&delegate)); } // sanity checks - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), delegated_amount); - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_amount); + assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); delegated_amount } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 6201d3f30ca2..41478fa0ec6e 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -24,43 +24,43 @@ use pallet_staking::Error as StakingError; use sp_staking::delegation::StakingDelegationSupport; #[test] -fn create_a_delegatee_with_first_delegator() { +fn create_a_delegate_with_first_delegator() { ExtBuilder::default().build_and_execute(|| { - let delegatee: AccountId = 200; + let delegate: AccountId = 200; let reward_account: AccountId = 201; let delegator: AccountId = 202; // set intention to accept delegation. - fund(&delegatee, 1000); - assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); + fund(&delegate, 1000); + assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_account)); // delegate to this account fund(&delegator, 1000); - assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(&delegator, &delegate, 100)); // verify - assert!(DelegatedStaking::is_delegatee(&delegatee)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); + assert!(DelegatedStaking::is_delegate(&delegate)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 100); assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100); }); } #[test] -fn cannot_become_delegatee() { +fn cannot_become_delegate() { ExtBuilder::default().build_and_execute(|| { - // cannot set reward account same as delegatee account + // cannot set reward account same as delegate account assert_noop!( DelegatedStaking::accept_delegations(&100, &100), Error::::InvalidRewardDestination ); - // an existing validator cannot become delegatee + // an existing validator cannot become delegate assert_noop!( DelegatedStaking::accept_delegations(&mock::GENESIS_VALIDATOR, &100), Error::::AlreadyStaker ); - // an existing nominator cannot become delegatee + // an existing nominator cannot become delegate assert_noop!( DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_ONE, &100), Error::::AlreadyStaker @@ -75,28 +75,28 @@ fn cannot_become_delegatee() { #[test] fn create_multiple_delegators() { ExtBuilder::default().build_and_execute(|| { - let delegatee: AccountId = 200; + let delegate: AccountId = 200; let reward_account: AccountId = 201; - // stakeable balance is 0 for non delegatee - fund(&delegatee, 1000); - assert!(!DelegatedStaking::is_delegatee(&delegatee)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); + // stakeable balance is 0 for non delegate + fund(&delegate, 1000); + assert!(!DelegatedStaking::is_delegate(&delegate)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); // set intention to accept delegation. - assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_account)); + assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_account)); // create 100 delegators for i in 202..302 { fund(&i, 100 + ExistentialDeposit::get()); - assert_ok!(DelegatedStaking::delegate(&i, &delegatee, 100)); - // Balance of 100 held on delegator account for delegating to the delegatee. + assert_ok!(DelegatedStaking::delegate(&i, &delegate, 100)); + // Balance of 100 held on delegator account for delegating to the delegate. assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &i), 100); } // verify - assert!(DelegatedStaking::is_delegatee(&delegatee)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100 * 100); + assert!(DelegatedStaking::is_delegate(&delegate)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 100 * 100); }); } @@ -104,40 +104,40 @@ fn create_multiple_delegators() { fn delegate_restrictions() { // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| { - let delegatee_one = 200; + let delegate_one = 200; let delegator_one = 210; - fund(&delegatee_one, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegatee_one, &(delegatee_one + 1))); + fund(&delegate_one, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegate_one, &(delegate_one + 1))); fund(&delegator_one, 200); - assert_ok!(DelegatedStaking::delegate(&delegator_one, &delegatee_one, 100)); + assert_ok!(DelegatedStaking::delegate(&delegator_one, &delegate_one, 100)); - let delegatee_two = 300; + let delegate_two = 300; let delegator_two = 310; - fund(&delegatee_two, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegatee_two, &(delegatee_two + 1))); + fund(&delegate_two, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegate_two, &(delegate_two + 1))); fund(&delegator_two, 200); - assert_ok!(DelegatedStaking::delegate(&delegator_two, &delegatee_two, 100)); + assert_ok!(DelegatedStaking::delegate(&delegator_two, &delegate_two, 100)); - // delegatee one tries to delegate to delegatee 2 + // delegate one tries to delegate to delegate 2 assert_noop!( - DelegatedStaking::delegate(&delegatee_one, &delegatee_two, 10), + DelegatedStaking::delegate(&delegate_one, &delegate_two, 10), Error::::InvalidDelegation ); - // delegatee one tries to delegate to a delegator + // delegate one tries to delegate to a delegator assert_noop!( - DelegatedStaking::delegate(&delegatee_one, &delegator_one, 10), - Error::::NotDelegatee + DelegatedStaking::delegate(&delegate_one, &delegator_one, 10), + Error::::NotDelegate ); assert_noop!( - DelegatedStaking::delegate(&delegatee_one, &delegator_two, 10), - Error::::NotDelegatee + DelegatedStaking::delegate(&delegate_one, &delegator_two, 10), + Error::::NotDelegate ); - // delegator one tries to delegate to delegatee 2 as well (it already delegates to delegatee + // delegator one tries to delegate to delegate 2 as well (it already delegates to delegate // 1) assert_noop!( - DelegatedStaking::delegate(&delegator_one, &delegatee_two, 10), + DelegatedStaking::delegate(&delegator_one, &delegate_two, 10), Error::::InvalidDelegation ); }); @@ -157,37 +157,37 @@ mod integration { #[test] fn bond() { ExtBuilder::default().build_and_execute(|| { - let delegatee: AccountId = 99; + let delegate: AccountId = 99; let reward_acc: AccountId = 100; - assert_eq!(Staking::status(&delegatee), Err(StakingError::::NotStash.into())); + assert_eq!(Staking::status(&delegate), Err(StakingError::::NotStash.into())); - // set intention to become a delegatee - fund(&delegatee, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegatee, &reward_acc)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); + // set intention to become a delegate + fund(&delegate, 100); + assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_acc)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); let mut delegated_balance: Balance = 0; // set some delegations for delegator in 200..250 { fund(&delegator, 200); - assert_ok!(DelegatedStaking::delegate(&delegator, &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(&delegator, &delegate, 100)); delegated_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100 ); - assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), delegated_balance); + assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_balance); // unbonded balance is the newly delegated 100 - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); - assert_ok!(DelegatedStaking::bond_all(&delegatee)); + assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); + assert_ok!(DelegatedStaking::bond_all(&delegate)); // after bond, unbonded balance is 0 - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); } assert_eq!( - Staking::stake(&delegatee).unwrap(), + Staking::stake(&delegate).unwrap(), Stake { total: 50 * 100, active: 50 * 100 } ) }); @@ -198,89 +198,89 @@ mod integration { ExtBuilder::default().build_and_execute(|| { // initial era start_era(1); - let delegatee: AccountId = 200; + let delegate: AccountId = 200; let reward_acc: AccountId = 201; let delegators: Vec = (301..=350).collect(); let total_staked = - setup_delegation_stake(delegatee, reward_acc, delegators.clone(), 10, 10); + setup_delegation_stake(delegate, reward_acc, delegators.clone(), 10, 10); // lets go to a new era start_era(2); - assert!(eq_stake(delegatee, total_staked, total_staked)); + assert!(eq_stake(delegate, total_staked, total_staked)); // Withdrawing without unbonding would fail. assert_noop!( - DelegatedStaking::withdraw(&delegatee, &300, 50, 0), + DelegatedStaking::withdraw(&delegate, &300, 50, 0), Error::::WithdrawFailed ); - // assert_noop!(DelegatedStaking::withdraw(&delegatee, &200, 50, 0), + // assert_noop!(DelegatedStaking::withdraw(&delegate, &200, 50, 0), // Error::::NotAllowed); active and total stake remains same - assert!(eq_stake(delegatee, total_staked, total_staked)); + assert!(eq_stake(delegate, total_staked, total_staked)); // 305 wants to unbond 50 in era 2, withdrawable in era 5. - assert_ok!(DelegatedStaking::unbond(&delegatee, 50)); + assert_ok!(DelegatedStaking::unbond(&delegate, 50)); // 310 wants to unbond 100 in era 3, withdrawable in era 6. start_era(3); - assert_ok!(DelegatedStaking::unbond(&delegatee, 100)); + assert_ok!(DelegatedStaking::unbond(&delegate, 100)); // 320 wants to unbond 200 in era 4, withdrawable in era 7. start_era(4); - assert_ok!(DelegatedStaking::unbond(&delegatee, 200)); + assert_ok!(DelegatedStaking::unbond(&delegate, 200)); // active stake is now reduced.. let expected_active = total_staked - (50 + 100 + 200); - assert!(eq_stake(delegatee, total_staked, expected_active)); + assert!(eq_stake(delegate, total_staked, expected_active)); // nothing to withdraw at era 4 assert_noop!( - DelegatedStaking::withdraw(&delegatee, &305, 50, 0), + DelegatedStaking::withdraw(&delegate, &305, 50, 0), Error::::WithdrawFailed ); - assert!(eq_stake(delegatee, total_staked, expected_active)); - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + assert!(eq_stake(delegate, total_staked, expected_active)); + assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); // full amount is still delegated - assert_eq!(DelegatedStaking::delegated_balance(&delegatee), total_staked); + assert_eq!(DelegatedStaking::delegated_balance(&delegate), total_staked); start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. assert_noop!( - DelegatedStaking::withdraw(&delegatee, &305, 51, 0), + DelegatedStaking::withdraw(&delegate, &305, 51, 0), Error::::WithdrawFailed ); // less is possible - assert_ok!(DelegatedStaking::withdraw(&delegatee, &305, 30, 0)); - assert_ok!(DelegatedStaking::withdraw(&delegatee, &305, 20, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegate, &305, 30, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegate, &305, 20, 0)); // Lets go to future era where everything is unbonded. Withdrawable amount: 100 + 200 start_era(7); // 305 has no more amount delegated so it cannot withdraw. assert_noop!( - DelegatedStaking::withdraw(&delegatee, &305, 5, 0), + DelegatedStaking::withdraw(&delegate, &305, 5, 0), Error::::NotDelegator ); // 309 is an active delegator but has total delegation of 90, so it cannot withdraw more // than that. assert_noop!( - DelegatedStaking::withdraw(&delegatee, &309, 91, 0), + DelegatedStaking::withdraw(&delegate, &309, 91, 0), Error::::NotEnoughFunds ); // 310 cannot withdraw more than delegated funds. assert_noop!( - DelegatedStaking::withdraw(&delegatee, &310, 101, 0), + DelegatedStaking::withdraw(&delegate, &310, 101, 0), Error::::NotEnoughFunds ); // but can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::withdraw(&delegatee, &310, 100, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegate, &310, 100, 0)); // 320 can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::withdraw(&delegatee, &320, 200, 0)); + assert_ok!(DelegatedStaking::withdraw(&delegate, &320, 200, 0)); // cannot withdraw anything more.. assert_noop!( - DelegatedStaking::withdraw(&delegatee, &301, 1, 0), + DelegatedStaking::withdraw(&delegate, &301, 1, 0), Error::::WithdrawFailed ); assert_noop!( - DelegatedStaking::withdraw(&delegatee, &350, 1, 0), + DelegatedStaking::withdraw(&delegate, &350, 1, 0), Error::::WithdrawFailed ); }); @@ -289,25 +289,25 @@ mod integration { #[test] fn withdraw_happens_with_unbonded_balance_first() { ExtBuilder::default().build_and_execute(|| { - let delegatee = 200; - setup_delegation_stake(delegatee, 201, (300..350).collect(), 100, 0); + let delegate = 200; + setup_delegation_stake(delegate, 201, (300..350).collect(), 100, 0); // verify withdraw not possible yet assert_noop!( - DelegatedStaking::withdraw(&delegatee, &300, 100, 0), + DelegatedStaking::withdraw(&delegate, &300, 100, 0), Error::::WithdrawFailed ); // add new delegation that is not staked fund(&300, 1000); - assert_ok!(DelegatedStaking::delegate(&300, &delegatee, 100)); + assert_ok!(DelegatedStaking::delegate(&300, &delegate, 100)); // verify unbonded balance - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 100); + assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); // withdraw works now without unbonding - assert_ok!(DelegatedStaking::withdraw(&delegatee, &300, 100, 0)); - assert_eq!(DelegatedStaking::unbonded_balance(&delegatee), 0); + assert_ok!(DelegatedStaking::withdraw(&delegate, &300, 100, 0)); + assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); }); } @@ -318,7 +318,7 @@ mod integration { fund(&200, 1000); let balance_200 = Balances::free_balance(200); - // delegatee cannot be reward destination + // `delegate` account cannot be reward destination assert_noop!( DelegatedStaking::accept_delegations(&200, &200), Error::::InvalidRewardDestination @@ -330,7 +330,7 @@ mod integration { fund(&300, 1000); assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); - // if delegatee calls Staking pallet directly with a different reward destination, it + // if delegate calls Staking pallet directly with a different reward destination, it // fails. assert_noop!( Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Stash), @@ -352,7 +352,7 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); assert_eq!(DelegatedStaking::delegated_balance(&200), 100); - // free balance of delegatee is untouched + // free balance of delegate is untouched assert_eq!(Balances::free_balance(200), balance_200); // trying to change reward destination later directly via staking does not work. @@ -368,15 +368,15 @@ mod integration { } #[test] - fn delegatee_restrictions() { + fn delegate_restrictions() { ExtBuilder::default().build_and_execute(|| { setup_delegation_stake(200, 201, (202..203).collect(), 100, 0); // Registering again is noop assert_noop!(DelegatedStaking::accept_delegations(&200, &201), Error::::NotAllowed); - // a delegator cannot become delegatee + // a delegator cannot become delegate assert_noop!(DelegatedStaking::accept_delegations(&202, &203), Error::::NotAllowed); - // existing staker cannot become a delegatee + // existing staker cannot become a delegate assert_noop!( DelegatedStaking::accept_delegations(&GENESIS_NOMINATOR_ONE, &201), Error::::AlreadyStaker @@ -397,7 +397,7 @@ mod integration { fund(&300, 1000); assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); - // delegatee blocks delegation + // delegate blocks delegation assert_ok!(DelegatedStaking::block_delegations(&200)); // cannot delegate to it anymore @@ -406,7 +406,7 @@ mod integration { Error::::DelegationsBlocked ); - // delegatee can unblock delegation + // delegate can unblock delegation assert_ok!(DelegatedStaking::unblock_delegations(&200)); // delegation works again @@ -420,7 +420,7 @@ mod integration { setup_delegation_stake(200, 201, (210..250).collect(), 100, 0); start_era(1); - // delegatee is slashed + // delegate is slashed todo!() }); } @@ -440,7 +440,7 @@ mod integration { let init_stake = Staking::stake(&200).unwrap(); // scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304) - // in equal parts. lets try to migrate this nominator into delegatee based stake. + // in equal parts. lets try to migrate this nominator into delegate based stake. // all balance currently is in 200 assert_eq!(Balances::free_balance(200), 5000); @@ -483,7 +483,7 @@ mod integration { expected_proxy_delegated_amount ); - // delegatee stake is unchanged. + // delegate stake is unchanged. assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 01f704076c7d..05bb28193a6b 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1098,7 +1098,7 @@ impl Pallet { } pub(crate) fn stakeable_balance(who: &T::AccountId) -> BalanceOf { - if T::DelegationSupport::is_delegatee(who) { + if T::DelegationSupport::is_delegate(who) { return T::DelegationSupport::stakeable_balance(who); } @@ -1109,7 +1109,7 @@ impl Pallet { who: &T::AccountId, reward_destination: Option, ) -> bool { - if T::DelegationSupport::is_delegatee(who) { + if T::DelegationSupport::is_delegate(who) { return T::DelegationSupport::restrict_reward_destination(who, reward_destination); } @@ -1120,7 +1120,7 @@ impl Pallet { who: &T::AccountId, amount: BalanceOf, ) -> sp_runtime::DispatchResult { - if T::DelegationSupport::is_delegatee(who) { + if T::DelegationSupport::is_delegate(who) { return T::DelegationSupport::update_hold(who, amount); } @@ -1882,7 +1882,7 @@ impl StakingDelegationSupport for NoDelegation { fn stakeable_balance(_who: &Self::AccountId) -> Self::Balance { BalanceOf::::zero() } - fn is_delegatee(_who: &Self::AccountId) -> bool { + fn is_delegate(_who: &Self::AccountId) -> bool { false } fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index 2eeee0e8a3d6..b36f66d634d6 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -608,7 +608,7 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; - let lazy_slash = T::DelegationSupport::is_delegatee(stash); + let lazy_slash = T::DelegationSupport::is_delegate(stash); let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); if value.is_zero() { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index da2ec2742750..e727d254f4cb 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -44,60 +44,61 @@ pub trait DelegationInterface { /// Set intention to accept delegations. fn accept_delegations( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, reward_destination: &Self::AccountId, ) -> DispatchResult; - /// Migrate an nominator account into a delegatee. + /// Migrate a nominator account into a `delegate`. /// /// # Arguments /// - /// * `new_delegatee`: This is the current nominator account. Funds will be moved from this - /// account to `proxy_delegator` and delegated back to `new_delegatee`. + /// * `new_delegate`: This is the current nominator account. Funds will be moved from this + /// account to `proxy_delegator` and delegated back to `new_delegate`. /// * `proxy_delegator`: All existing staked funds will be moved to this account. Future /// migration of funds from `proxy_delegator` to `delegator` is possible via calling /// [`Self::migrate_delegator`]. - /// * `payee`: Delegatees need to set where they want their rewards to be paid out. + /// * `payee`: `Delegate` needs to set where they want their rewards to be paid out. This can be + /// anything other than the `delegate` account itself. /// /// This is similar to [`Self::accept_delegations`] but allows a current nominator to migrate to - /// a delegatee. + /// a `delegate`. fn migrate_accept_delegations( - new_delegatee: &Self::AccountId, + new_delegate: &Self::AccountId, proxy_delegator: &Self::AccountId, payee: &Self::AccountId, ) -> DispatchResult; /// Stop accepting new delegations to this account. - fn block_delegations(delegatee: &Self::AccountId) -> DispatchResult; + fn block_delegations(delegate: &Self::AccountId) -> DispatchResult; /// Unblock delegations to this account. - fn unblock_delegations(delegatee: &Self::AccountId) -> DispatchResult; + fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult; - /// Remove oneself as Delegatee. + /// Remove oneself as a `delegate`. /// - /// This will only succeed if all delegations to this delegatee are withdrawn. - fn kill_delegatee(delegatee: &Self::AccountId) -> DispatchResult; + /// This will only succeed if all delegations to this `delegate` are withdrawn. + fn kill_delegate(delegate: &Self::AccountId) -> DispatchResult; /// Bond all fund that is delegated but not staked. /// FIXME(ank4n): Should not be allowed as withdrawn funds would get restaked. - fn bond_all(delegatee: &Self::AccountId) -> DispatchResult; + fn bond_all(delegate: &Self::AccountId) -> DispatchResult; - /// Request withdrawal of unbonded stake of `delegatee` belonging to the provided `delegator`. + /// Request withdrawal of unbonded stake of `delegate` belonging to the provided `delegator`. /// - /// Important: It is upto `delegatee` to enforce which `delegator` can withdraw `value`. The + /// Important: It is upto `delegate` to enforce which `delegator` can withdraw `value`. The /// withdrawn value is released in `delegator`'s account. fn withdraw( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, num_slashing_spans: u32, ) -> DispatchResult; - /// Applies a pending slash on delegatee by passing a delegator account who should be slashed + /// Applies a pending slash on `delegate` by passing a delegator account who should be slashed /// and the value to be slashed. Optionally also takes a reporter account who will be rewarded /// from part of the slash imbalance. fn apply_slash( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, reporter: Option, @@ -105,19 +106,19 @@ pub trait DelegationInterface { /// Move a delegated amount from `proxy_delegator` to `new_delegator`. /// - /// Delegatee must have used [`Self::migrate_accept_delegations`] to setup a `proxy_delegator`. + /// `Delegate` must have used [`Self::migrate_accept_delegations`] to setup a `proxy_delegator`. /// This is useful for migrating old pool accounts using direct staking to lazily move /// delegators to the new delegated pool account. fn migrate_delegator( - delegatee: &Self::AccountId, + delegate: &Self::AccountId, new_delegator: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; - /// As a `delegator`, delegate some funds to a Delegatee + /// Delegate some funds to a `delegate` account. fn delegate( delegator: &Self::AccountId, - delegatee: &Self::AccountId, + delegate: &Self::AccountId, value: Self::Balance, ) -> DispatchResult; } @@ -151,11 +152,11 @@ pub trait StakingDelegationSupport { } /// Returns true if `who` accepts delegations for stake. - fn is_delegatee(who: &Self::AccountId) -> bool; + fn is_delegate(who: &Self::AccountId) -> bool; /// Update amount held for bonded stake. fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; - /// Reports an ongoing slash to the delegatee account that would be applied lazily. + /// Reports an ongoing slash to the `delegate` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index c0c62c3786e2..4b6115ce02c9 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -293,7 +293,7 @@ pub trait StakingInterface { /// Release all funds bonded for stake. /// - /// Unsafe, only used for migration of delegatee accounts. + /// Unsafe, only used for migration of `delegate` accounts. fn release_all(who: &Self::AccountId); #[cfg(feature = "runtime-benchmarks")] From 075e7935606845d456eac7b4057bc1d1bbfc855d Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 10:33:11 +0100 Subject: [PATCH 080/202] create pot accounts --- substrate/frame/delegated-staking/src/lib.rs | 35 ++++++++++++++++--- substrate/frame/delegated-staking/src/mock.rs | 5 +++ .../frame/delegated-staking/src/tests.rs | 30 ++++++++-------- substrate/frame/nomination-pools/src/lib.rs | 2 +- .../primitives/staking/src/delegation.rs | 2 +- 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 8e7fefae24bc..8057bdcd1279 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -20,7 +20,7 @@ //! An implementation of a delegation system for staking that can be utilised using //! [`DelegationInterface`]. In future, if exposed via extrinsic, these primitives could also be -//! used by off-chain entities, smart contracts or by other parachains via xcm. +//! used by off-chain entities, or by foreign multi-locations (via xcm). //! //! Delegate: Someone who accepts delegations. An account can set their intention to accept //! delegations by calling [`DelegationInterface::accept_delegations`]. This account cannot have @@ -69,7 +69,7 @@ use frame_support::{ transactional, }; -use sp_runtime::{traits::Zero, DispatchResult, Perbill, RuntimeDebug, Saturating}; +use sp_runtime::{traits::{Zero, AccountIdConversion}, DispatchResult, Perbill, RuntimeDebug, Saturating}; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, EraIndex, Stake, StakerStatus, StakingInterface, @@ -91,6 +91,10 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Injected identifier for the pallet. + #[pallet::constant] + type PalletId: Get; + type Currency: FunHoldMutate + FunMutate + FunHoldBalanced; @@ -207,19 +211,39 @@ pub struct DelegationLedger { pub pending_slash: BalanceOf, /// Whether this `delegate` is blocked from receiving new delegations. pub blocked: bool, + /// The `delegate` account associated with the ledger. + #[codec(skip)] + delegate: Option, +} + +/// The type of pot account being created. +#[derive(Encode, Decode)] +enum AccountType { + /// Funds that are withdrawn from the staking ledger but not claimed by the `delegator` yet. + UnclaimedWithdrawal, + /// A proxy delegator account created for a nominator who migrated to a `delegate` account. + /// + /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. + ProxyDelegator, } impl DelegationLedger { - /// balance that can be staked. + /// Balance that is stakeable. pub fn delegated_balance(&self) -> BalanceOf { // do not allow to stake more than unapplied slash self.total_delegated.saturating_sub(self.pending_slash) } - /// balance that is delegated but not bonded. + /// Balance that is delegated but not bonded. + /// + /// Can be funds that are unbonded but not withdrawn. pub fn unbonded_balance(&self) -> BalanceOf { self.total_delegated.saturating_sub(self.hold) } + + pub fn unclaimed_withdraw_account(&self) -> T::AccountId { + T::PalletId::get().into_sub_account_truncating(self.delegate.clone()) + } } impl DelegationInterface for Pallet { @@ -264,6 +288,7 @@ impl DelegationInterface for Pallet { hold: Zero::zero(), pending_slash: Zero::zero(), blocked: false, + delegate: Some(who.clone()), }, ); @@ -348,7 +373,7 @@ impl DelegationInterface for Pallet { } #[transactional] - fn withdraw( + fn delegate_withdraw( delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 82c31c66038d..f17c0cb2522d 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -21,6 +21,7 @@ use frame_support::{ pallet_prelude::*, parameter_types, traits::{ConstU64, Currency}, + PalletId, }; use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill}; @@ -134,8 +135,12 @@ impl pallet_staking::Config for Runtime { type WeightInfo = (); } +parameter_types! { + pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk"); +} impl delegated_staking::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type PalletId = DelegatedStakingPalletId; type Currency = Balances; type OnSlash = (); type RuntimeHoldReason = RuntimeHoldReason; diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 41478fa0ec6e..889d9601564b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -210,10 +210,10 @@ mod integration { assert!(eq_stake(delegate, total_staked, total_staked)); // Withdrawing without unbonding would fail. assert_noop!( - DelegatedStaking::withdraw(&delegate, &300, 50, 0), + DelegatedStaking::delegate_withdraw(&delegate, &300, 50, 0), Error::::WithdrawFailed ); - // assert_noop!(DelegatedStaking::withdraw(&delegate, &200, 50, 0), + // assert_noop!(DelegatedStaking::delegate_withdraw(&delegate, &200, 50, 0), // Error::::NotAllowed); active and total stake remains same assert!(eq_stake(delegate, total_staked, total_staked)); @@ -232,7 +232,7 @@ mod integration { // nothing to withdraw at era 4 assert_noop!( - DelegatedStaking::withdraw(&delegate, &305, 50, 0), + DelegatedStaking::delegate_withdraw(&delegate, &305, 50, 0), Error::::WithdrawFailed ); @@ -244,43 +244,43 @@ mod integration { start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. assert_noop!( - DelegatedStaking::withdraw(&delegate, &305, 51, 0), + DelegatedStaking::delegate_withdraw(&delegate, &305, 51, 0), Error::::WithdrawFailed ); // less is possible - assert_ok!(DelegatedStaking::withdraw(&delegate, &305, 30, 0)); - assert_ok!(DelegatedStaking::withdraw(&delegate, &305, 20, 0)); + assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &305, 30, 0)); + assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &305, 20, 0)); // Lets go to future era where everything is unbonded. Withdrawable amount: 100 + 200 start_era(7); // 305 has no more amount delegated so it cannot withdraw. assert_noop!( - DelegatedStaking::withdraw(&delegate, &305, 5, 0), + DelegatedStaking::delegate_withdraw(&delegate, &305, 5, 0), Error::::NotDelegator ); // 309 is an active delegator but has total delegation of 90, so it cannot withdraw more // than that. assert_noop!( - DelegatedStaking::withdraw(&delegate, &309, 91, 0), + DelegatedStaking::delegate_withdraw(&delegate, &309, 91, 0), Error::::NotEnoughFunds ); // 310 cannot withdraw more than delegated funds. assert_noop!( - DelegatedStaking::withdraw(&delegate, &310, 101, 0), + DelegatedStaking::delegate_withdraw(&delegate, &310, 101, 0), Error::::NotEnoughFunds ); // but can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::withdraw(&delegate, &310, 100, 0)); + assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &310, 100, 0)); // 320 can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::withdraw(&delegate, &320, 200, 0)); + assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &320, 200, 0)); // cannot withdraw anything more.. assert_noop!( - DelegatedStaking::withdraw(&delegate, &301, 1, 0), + DelegatedStaking::delegate_withdraw(&delegate, &301, 1, 0), Error::::WithdrawFailed ); assert_noop!( - DelegatedStaking::withdraw(&delegate, &350, 1, 0), + DelegatedStaking::delegate_withdraw(&delegate, &350, 1, 0), Error::::WithdrawFailed ); }); @@ -294,7 +294,7 @@ mod integration { // verify withdraw not possible yet assert_noop!( - DelegatedStaking::withdraw(&delegate, &300, 100, 0), + DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0), Error::::WithdrawFailed ); @@ -306,7 +306,7 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); // withdraw works now without unbonding - assert_ok!(DelegatedStaking::withdraw(&delegate, &300, 100, 0)); + assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0)); assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); }); } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 5182f69c32cd..fef5cfaccf5b 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -2287,7 +2287,7 @@ pub mod pallet { // `transferrable_balance` is correct. // fixme(ank4n): Handle result. let _withdraw_result = - T::Staking::withdraw(&bonded_pool.bonded_account(), &member_account, balance_to_unbond, num_slashing_spans)?; + T::Staking::delegate_withdraw(&bonded_pool.bonded_account(), &member_account, balance_to_unbond, num_slashing_spans)?; // defensive-only: the depositor puts enough funds into the stash so that it will only // be destroyed when they are leaving. diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index e727d254f4cb..6aa4befa339d 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -87,7 +87,7 @@ pub trait DelegationInterface { /// /// Important: It is upto `delegate` to enforce which `delegator` can withdraw `value`. The /// withdrawn value is released in `delegator`'s account. - fn withdraw( + fn delegate_withdraw( delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, From b4918e745b289f93d439600ed2f213a784273c4d Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 11:16:56 +0100 Subject: [PATCH 081/202] add call signatures --- substrate/frame/delegated-staking/src/lib.rs | 81 ++++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 8057bdcd1279..a900bd9c7a95 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -67,9 +67,13 @@ use frame_support::{ DefensiveOption, Imbalance, OnUnbalanced, }, transactional, + weights::Weight, }; -use sp_runtime::{traits::{Zero, AccountIdConversion}, DispatchResult, Perbill, RuntimeDebug, Saturating}; +use sp_runtime::{ + traits::{AccountIdConversion, Zero}, + DispatchResult, Perbill, RuntimeDebug, Saturating, +}; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, EraIndex, Stake, StakerStatus, StakingInterface, @@ -82,6 +86,7 @@ pub type BalanceOf = #[frame_support::pallet] pub mod pallet { use super::*; + use frame_system::{ensure_signed, pallet_prelude::*}; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -187,6 +192,74 @@ pub mod pallet { #[pallet::storage] pub(crate) type DelegateMigration = CountedStorageMap<_, Twox64Concat, T::AccountId, T::AccountId, OptionQuery>; + + #[pallet::call] + impl Pallet { + /// Register an account to be a `Delegate`. + /// + /// `Delegate` accounts accepts delegations from other `delegator`s and stake funds on their + /// behalf. + #[pallet::call_index(0)] + #[pallet::weight(Weight::default())] + pub fn register_as_delegate( + origin: OriginFor, + reward_account: T::AccountId, + ) -> DispatchResult { + todo!() + } + + /// Migrate from a `Nominator` account to `Delegate` account. + #[pallet::call_index(1)] + #[pallet::weight(Weight::default())] + pub fn migrate_to_delegate( + origin: OriginFor, + reward_account: T::AccountId, + ) -> DispatchResult { + todo!() + } + + /// Release delegated amount to delegator. + /// + /// Tries to withdraw unbonded fund if needed from staking and release amount to delegator. + /// + /// Only `delegate` account can call this. + #[pallet::call_index(2)] + #[pallet::weight(Weight::default())] + pub fn release ( + origin: OriginFor, + delegator: T::AccountId, + amount: BalanceOf, + num_slashing_spans: u32, + ) -> DispatchResult { + todo!() + } + + /// Migrate delegated fund. + /// + /// This moves delegator funds from `pxoxy_delegator` account to `delegator` account. + /// + /// Only `delegate` account can call this. + #[pallet::call_index(3)] + #[pallet::weight(Weight::default())] + pub fn migrate_delegation(origin: OriginFor, delegator: T::AccountId, amount: BalanceOf) -> DispatchResult{ + todo!() + } + + /// Delegate funds to a `Delegate` account. + #[pallet::call_index(4)] + #[pallet::weight(Weight::default())] + // FIXME(ank4n): rename to `delegate` + pub fn delegate_funds(origin: OriginFor, delegate: T::AccountId, amount: BalanceOf) -> DispatchResult { + todo!() + } + + /// Add funds to an existing delegation. + #[pallet::call_index(5)] + #[pallet::weight(Weight::default())] + pub fn delegate_extra(origin: OriginFor, delegate: T::AccountId, amount: BalanceOf) -> DispatchResult { + todo!() + } + } } /// Register of all delegations to a `Delegate`. @@ -256,8 +329,7 @@ impl DelegationInterface for Pallet { } fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) + >::get(who).map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) } fn accept_delegations( @@ -568,8 +640,7 @@ impl StakingDelegationSupport for Pallet { ensure!(Self::is_delegate(who), Error::::NotSupported); // delegation register should exist since `who` is a delegate. - let delegation_register = - >::get(who).defensive_ok_or(Error::::BadState)?; + let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); From a635f1db3477eaa69a32542e5d0265d0a68917dc Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 11:38:34 +0100 Subject: [PATCH 082/202] return correct error --- substrate/frame/staking/src/pallet/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 05bb28193a6b..3c6c24136a86 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1887,7 +1887,7 @@ impl StakingDelegationSupport for NoDelegation { } fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { defensive!("delegation update_hold should not be have been called for NoDelegation"); - Err(Error::::NotEnoughFunds.into()) + Err(Error::::BadState.into()) } fn report_slash(_who: &Self::AccountId, _slash: Self::Balance) { From 309ddf5a827e8a4cf60d48388265d3db874f1790 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 12:32:43 +0100 Subject: [PATCH 083/202] call register as delegate --- substrate/frame/delegated-staking/src/lib.rs | 92 +++++++++++++------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index a900bd9c7a95..7a897d96703e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -83,10 +83,11 @@ use sp_std::{convert::TryInto, prelude::*}; pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; +use frame_system::{ensure_signed, pallet_prelude::*, RawOrigin}; + #[frame_support::pallet] pub mod pallet { use super::*; - use frame_system::{ensure_signed, pallet_prelude::*}; #[pallet::pallet] pub struct Pallet(PhantomData); @@ -180,7 +181,7 @@ pub mod pallet { pub(crate) type Delegators = CountedStorageMap<_, Twox64Concat, T::AccountId, (T::AccountId, BalanceOf), OptionQuery>; - /// Map of Delegate to their Ledger. + /// Map of `Delegate` to their Ledger. #[pallet::storage] pub(crate) type Delegates = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; @@ -205,7 +206,23 @@ pub mod pallet { origin: OriginFor, reward_account: T::AccountId, ) -> DispatchResult { - todo!() + let who = ensure_signed(origin)?; + + // Existing `delegate` cannot register again. + ensure!(!Self::is_delegate(&who), Error::::NotAllowed); + + // A delegator cannot become a `delegate`. + ensure!(!Self::is_delegator(&who), Error::::NotAllowed); + + // payee account cannot be same as `delegate` + ensure!(reward_account != who, Error::::InvalidRewardDestination); + + // They cannot be already a direct staker in the staking pallet. + ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); + + DelegationLedger::::new(&reward_account).save(&who); + + Ok(()) } /// Migrate from a `Nominator` account to `Delegate` account. @@ -301,6 +318,29 @@ enum AccountType { } impl DelegationLedger { + pub fn new(reward_destination: &T::AccountId) -> Self { + DelegationLedger { + payee: reward_destination.clone(), + total_delegated: Zero::zero(), + hold: Zero::zero(), + pending_slash: Zero::zero(), + blocked: false, + delegate: None, + } + } + + /// consumes self and returns a new copy with the key. + fn with_key(self, key: &T::AccountId) -> Self { + DelegationLedger { + delegate: Some(key.clone()), + ..self + } + } + + fn get(key: &T::AccountId) -> Option>{ + >::get(key).map(|d| d.with_key(key)) + } + /// Balance that is stakeable. pub fn delegated_balance(&self) -> BalanceOf { // do not allow to stake more than unapplied slash @@ -317,6 +357,16 @@ impl DelegationLedger { pub fn unclaimed_withdraw_account(&self) -> T::AccountId { T::PalletId::get().into_sub_account_truncating(self.delegate.clone()) } + + pub fn save(self, key: &T::AccountId) -> Self { + let new_val = self.with_key(key); + >::insert( + key, + &new_val, + ); + + new_val + } } impl DelegationInterface for Pallet { @@ -336,35 +386,7 @@ impl DelegationInterface for Pallet { who: &Self::AccountId, reward_destination: &Self::AccountId, ) -> DispatchResult { - // Existing `delegate` cannot register again. - ensure!(!>::contains_key(who), Error::::NotAllowed); - - // A delegator cannot become a `delegate`. - ensure!(!>::contains_key(who), Error::::NotAllowed); - - // payee account cannot be same as `delegate` - ensure!(reward_destination != who, Error::::InvalidRewardDestination); - - // make sure they are not already a direct staker or they are migrating. - ensure!( - T::CoreStaking::status(who).is_err() || >::contains_key(who), - Error::::AlreadyStaker - ); - - // already checked that `delegate` exist - >::insert( - who, - DelegationLedger { - payee: reward_destination.clone(), - total_delegated: Zero::zero(), - hold: Zero::zero(), - pending_slash: Zero::zero(), - blocked: false, - delegate: Some(who.clone()), - }, - ); - - Ok(()) + Self::register_as_delegate(RawOrigin::Signed(who.clone()).into(), reward_destination.clone()) } /// Transfers funds from current staked account to `proxy_delegator`. Current staked account @@ -857,6 +879,12 @@ impl Pallet { >::contains_key(delegate) } + + /// Returns true if who is not already staking. + fn not_direct_staker(who: &T::AccountId) -> bool { + T::CoreStaking::status(&who).is_err() + } + fn delegation_withdraw( delegator: &T::AccountId, delegate: &T::AccountId, From ea5d777223e537b6f2c70983dc3acfe523654604 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 17:34:29 +0100 Subject: [PATCH 084/202] add mod for types --- substrate/frame/delegated-staking/src/lib.rs | 220 ++++++++++-------- .../frame/delegated-staking/src/types.rs | 112 +++++++++ substrate/primitives/staking/src/lib.rs | 2 +- 3 files changed, 232 insertions(+), 102 deletions(-) create mode 100644 substrate/frame/delegated-staking/src/types.rs diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 7a897d96703e..2c426dfdec75 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -53,6 +53,10 @@ mod tests; pub use pallet::*; +mod types; + +use types::*; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; @@ -208,31 +212,39 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; + // They cannot be already a direct staker in the staking pallet. + ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); + // Existing `delegate` cannot register again. ensure!(!Self::is_delegate(&who), Error::::NotAllowed); // A delegator cannot become a `delegate`. ensure!(!Self::is_delegator(&who), Error::::NotAllowed); - // payee account cannot be same as `delegate` + // Reward account cannot be same as `delegate` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); - // They cannot be already a direct staker in the staking pallet. - ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); - DelegationLedger::::new(&reward_account).save(&who); Ok(()) } /// Migrate from a `Nominator` account to `Delegate` account. + /// + /// Internally transfers minimum balance to a proxy delegator account created for it. #[pallet::call_index(1)] #[pallet::weight(Weight::default())] pub fn migrate_to_delegate( origin: OriginFor, reward_account: T::AccountId, ) -> DispatchResult { - todo!() + let who = ensure_signed(origin)?; + ensure!(Self::is_direct_nominator(&who), Error::::NotAllowed); + + // Reward account cannot be same as `delegate` account. + ensure!(reward_account != who, Error::::InvalidRewardDestination); + + Self::do_migrate_to_delegate(&who, &reward_account) } /// Release delegated amount to delegator. @@ -242,7 +254,7 @@ pub mod pallet { /// Only `delegate` account can call this. #[pallet::call_index(2)] #[pallet::weight(Weight::default())] - pub fn release ( + pub fn release( origin: OriginFor, delegator: T::AccountId, amount: BalanceOf, @@ -258,7 +270,11 @@ pub mod pallet { /// Only `delegate` account can call this. #[pallet::call_index(3)] #[pallet::weight(Weight::default())] - pub fn migrate_delegation(origin: OriginFor, delegator: T::AccountId, amount: BalanceOf) -> DispatchResult{ + pub fn migrate_delegation( + origin: OriginFor, + delegator: T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { todo!() } @@ -266,109 +282,27 @@ pub mod pallet { #[pallet::call_index(4)] #[pallet::weight(Weight::default())] // FIXME(ank4n): rename to `delegate` - pub fn delegate_funds(origin: OriginFor, delegate: T::AccountId, amount: BalanceOf) -> DispatchResult { + pub fn delegate_funds( + origin: OriginFor, + delegate: T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { todo!() } /// Add funds to an existing delegation. #[pallet::call_index(5)] #[pallet::weight(Weight::default())] - pub fn delegate_extra(origin: OriginFor, delegate: T::AccountId, amount: BalanceOf) -> DispatchResult { + pub fn delegate_extra( + origin: OriginFor, + delegate: T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { todo!() } } } -/// Register of all delegations to a `Delegate`. -/// -/// This keeps track of the active balance of the `delegate` that is made up from the funds that are -/// currently delegated to this `delegate`. It also tracks the pending slashes yet to be applied -/// among other things. -#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -pub struct DelegationLedger { - /// Where the reward should be paid out. - pub payee: T::AccountId, - /// Sum of all delegated funds to this `delegate`. - #[codec(compact)] - pub total_delegated: BalanceOf, - /// Amount that is bonded and held. - // FIXME(ank4n) (can we remove it) - #[codec(compact)] - pub hold: BalanceOf, - /// Slashes that are not yet applied. - #[codec(compact)] - pub pending_slash: BalanceOf, - /// Whether this `delegate` is blocked from receiving new delegations. - pub blocked: bool, - /// The `delegate` account associated with the ledger. - #[codec(skip)] - delegate: Option, -} - -/// The type of pot account being created. -#[derive(Encode, Decode)] -enum AccountType { - /// Funds that are withdrawn from the staking ledger but not claimed by the `delegator` yet. - UnclaimedWithdrawal, - /// A proxy delegator account created for a nominator who migrated to a `delegate` account. - /// - /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. - ProxyDelegator, -} - -impl DelegationLedger { - pub fn new(reward_destination: &T::AccountId) -> Self { - DelegationLedger { - payee: reward_destination.clone(), - total_delegated: Zero::zero(), - hold: Zero::zero(), - pending_slash: Zero::zero(), - blocked: false, - delegate: None, - } - } - - /// consumes self and returns a new copy with the key. - fn with_key(self, key: &T::AccountId) -> Self { - DelegationLedger { - delegate: Some(key.clone()), - ..self - } - } - - fn get(key: &T::AccountId) -> Option>{ - >::get(key).map(|d| d.with_key(key)) - } - - /// Balance that is stakeable. - pub fn delegated_balance(&self) -> BalanceOf { - // do not allow to stake more than unapplied slash - self.total_delegated.saturating_sub(self.pending_slash) - } - - /// Balance that is delegated but not bonded. - /// - /// Can be funds that are unbonded but not withdrawn. - pub fn unbonded_balance(&self) -> BalanceOf { - self.total_delegated.saturating_sub(self.hold) - } - - pub fn unclaimed_withdraw_account(&self) -> T::AccountId { - T::PalletId::get().into_sub_account_truncating(self.delegate.clone()) - } - - pub fn save(self, key: &T::AccountId) -> Self { - let new_val = self.with_key(key); - >::insert( - key, - &new_val, - ); - - new_val - } -} - impl DelegationInterface for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -386,7 +320,10 @@ impl DelegationInterface for Pallet { who: &Self::AccountId, reward_destination: &Self::AccountId, ) -> DispatchResult { - Self::register_as_delegate(RawOrigin::Signed(who.clone()).into(), reward_destination.clone()) + Self::register_as_delegate( + RawOrigin::Signed(who.clone()).into(), + reward_destination.clone(), + ) } /// Transfers funds from current staked account to `proxy_delegator`. Current staked account @@ -867,6 +804,10 @@ impl StakingInterface for Pallet { } } impl Pallet { + fn sub_account(account_type: AccountType, delegate_account: T::AccountId) -> T::AccountId { + T::PalletId::get().into_sub_account_truncating((account_type, delegate_account.clone())) + } + fn is_delegate(who: &T::AccountId) -> bool { >::contains_key(who) } @@ -879,12 +820,89 @@ impl Pallet { >::contains_key(delegate) } - /// Returns true if who is not already staking. fn not_direct_staker(who: &T::AccountId) -> bool { T::CoreStaking::status(&who).is_err() } + /// Returns true if who is not already staking. + fn is_direct_nominator(who: &T::AccountId) -> bool { + T::CoreStaking::status(who) + .map(|status| matches!(status, StakerStatus::Nominator(_))) + .unwrap_or(false) + } + + fn do_migrate_to_delegate(who: &T::AccountId, reward_account: &T::AccountId) -> DispatchResult { + // Get current stake + let stake = T::CoreStaking::stake(who)?; + + // release funds from core staking. + T::CoreStaking::release_all(who); + + // We create a proxy delegator that will keep all the delegation funds until funds are + // transferred to actual delegator. + let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, who.clone()); + + // transferring just released staked amount. This should never fail but if it does, it + // indicates bad state and we abort. + T::Currency::transfer(who, &proxy_delegator, stake.total, Preservation::Protect) + .map_err(|_| Error::::BadState)?; + + DelegationLedger::::new(&reward_account).save(&who); + // FIXME(ank4n) expose set payee in staking interface. + // T::CoreStaking::set_payee(who, reward_account) + + Self::do_delegate(&proxy_delegator, who, stake.total)?; + Self::do_bond(who, stake.total) + } + + fn do_bond(delegate: &T::AccountId, amount: BalanceOf) -> DispatchResult { + let ledger = >::get(delegate).defensive_ok_or(Error::::NotDelegate)?; + + debug_assert!(amount == ledger.unbonded_balance()); + + match T::CoreStaking::stake(delegate) { + // already bonded + Ok(_) => T::CoreStaking::bond_extra(delegate, amount), + // first bond + Err(_) => T::CoreStaking::bond(delegate, amount, &ledger.payee), + } + } + + fn do_delegate( + delegator: &T::AccountId, + delegate: &T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { + // let mut delegation_register = + // >::get(delegate).ok_or(Error::::NotDelegate)?; + // ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); + // + // let new_delegation_amount = + // if let Some((current_delegate, current_delegation)) = >::get(delegator) { + // ensure!(¤t_delegate == delegate, Error::::InvalidDelegation); + // value.saturating_add(current_delegation) + // } else { + // value + // }; + // + // delegation_register.total_delegated.saturating_accrue(value); + // + // >::insert(delegator, (delegate, new_delegation_amount)); + // >::insert(delegate, delegation_register); + // + // T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; + // + // Self::deposit_event(Event::::Delegated { + // delegate: delegate.clone(), + // delegator: delegator.clone(), + // amount: value, + // }); + // + // Ok(()) + + todo!() + } fn delegation_withdraw( delegator: &T::AccountId, delegate: &T::AccountId, diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs new file mode 100644 index 000000000000..6de552d23558 --- /dev/null +++ b/substrate/frame/delegated-staking/src/types.rs @@ -0,0 +1,112 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Basic types used in delegated staking. + +use super::*; + +/// The type of pot account being created. +#[derive(Encode, Decode)] +pub(crate) enum AccountType { + /// Funds that are withdrawn from the staking ledger but not claimed by the `delegator` yet. + UnclaimedWithdrawal, + /// A proxy delegator account created for a nominator who migrated to a `delegate` account. + /// + /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. + ProxyDelegator, +} + +#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct Delegation { + // The target of delegation. + pub delegate: T::AccountId, + // The amount delegated. + pub amount: BalanceOf, +} + +/// Ledger of all delegations to a `Delegate`. +/// +/// This keeps track of the active balance of the `delegate` that is made up from the funds that are +/// currently delegated to this `delegate`. It also tracks the pending slashes yet to be applied +/// among other things. +#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct DelegationLedger { + /// Where the reward should be paid out. + pub payee: T::AccountId, + /// Sum of all delegated funds to this `delegate`. + #[codec(compact)] + pub total_delegated: BalanceOf, + /// Amount that is bonded and held. + // FIXME(ank4n) (can we remove it) + #[codec(compact)] + pub hold: BalanceOf, + /// Slashes that are not yet applied. + #[codec(compact)] + pub pending_slash: BalanceOf, + /// Whether this `delegate` is blocked from receiving new delegations. + pub blocked: bool, + /// The `delegate` account associated with the ledger. + #[codec(skip)] + pub delegate: Option, +} + + + +impl DelegationLedger { + pub fn new(reward_destination: &T::AccountId) -> Self { + DelegationLedger { + payee: reward_destination.clone(), + total_delegated: Zero::zero(), + hold: Zero::zero(), + pending_slash: Zero::zero(), + blocked: false, + delegate: None, + } + } + + /// consumes self and returns a new copy with the key. + fn with_key(self, key: &T::AccountId) -> Self { + DelegationLedger { delegate: Some(key.clone()), ..self } + } + + fn get(key: &T::AccountId) -> Option { + >::get(key).map(|d| d.with_key(key)) + } + + /// Balance that is stakeable. + pub fn delegated_balance(&self) -> BalanceOf { + // do not allow to stake more than unapplied slash + self.total_delegated.saturating_sub(self.pending_slash) + } + + /// Balance that is delegated but not bonded. + /// + /// Can be funds that are unbonded but not withdrawn. + pub fn unbonded_balance(&self) -> BalanceOf { + self.total_delegated.saturating_sub(self.hold) + } + + pub fn save(self, key: &T::AccountId) -> Self { + let new_val = self.with_key(key); + >::insert(key, &new_val); + + new_val + } +} diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 4b6115ce02c9..4819ea583a27 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -272,7 +272,7 @@ pub trait StakingInterface { /// Checks whether an account `staker` has been exposed in an era. fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool; - /// Return the status of the given staker, `None` if not staked at all. + /// Return the status of the given staker, `Err` if not staked at all. fn status(who: &Self::AccountId) -> Result, DispatchError>; /// Checks whether or not this is a validator account. From 259b3a7eb7e9c6c23c8d933134187c35446d6ec8 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 17:42:57 +0100 Subject: [PATCH 085/202] move all public impls of traits to its own module --- .../frame/delegated-staking/src/impls.rs | 523 ++++++++++++++++++ substrate/frame/delegated-staking/src/lib.rs | 502 +---------------- 2 files changed, 526 insertions(+), 499 deletions(-) create mode 100644 substrate/frame/delegated-staking/src/impls.rs diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs new file mode 100644 index 000000000000..03fbe2437b6e --- /dev/null +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -0,0 +1,523 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Implementations of public traits, namely [StakingInterface], [DelegationInterface] and +//! [StakingDelegationSupport]. + +use super::*; + +/// StakingInterface implementation with delegation support. +/// +/// Only supports Nominators via Delegated Bonds. It is possible for a nominator to migrate and +/// become a `Delegate`. +impl StakingInterface for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + type CurrencyToVote = ::CurrencyToVote; + + fn minimum_nominator_bond() -> Self::Balance { + T::CoreStaking::minimum_nominator_bond() + } + + fn minimum_validator_bond() -> Self::Balance { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::minimum_validator_bond() + } + + fn stash_by_ctrl(_controller: &Self::AccountId) -> Result { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + // ctrl are deprecated, just return err. + Err(Error::::NotSupported.into()) + } + + fn bonding_duration() -> EraIndex { + T::CoreStaking::bonding_duration() + } + + fn current_era() -> EraIndex { + T::CoreStaking::current_era() + } + + fn stake(who: &Self::AccountId) -> Result, DispatchError> { + ensure!(Self::is_delegate(who), Error::::NotSupported); + return T::CoreStaking::stake(who); + } + + fn total_stake(who: &Self::AccountId) -> Result { + if Self::is_delegate(who) { + return T::CoreStaking::total_stake(who); + } + + if Self::is_delegator(who) { + let (_, delegation_amount) = + >::get(who).defensive_ok_or(Error::::BadState)?; + return Ok(delegation_amount) + } + + Err(Error::::NotSupported.into()) + } + + fn active_stake(who: &Self::AccountId) -> Result { + T::CoreStaking::active_stake(who) + } + + fn is_unbonding(who: &Self::AccountId) -> Result { + T::CoreStaking::is_unbonding(who) + } + + fn fully_unbond(who: &Self::AccountId) -> DispatchResult { + ensure!(Self::is_delegate(who), Error::::NotSupported); + return T::CoreStaking::fully_unbond(who); + } + + fn bond( + who: &Self::AccountId, + value: Self::Balance, + payee: &Self::AccountId, + ) -> DispatchResult { + // ensure who is not already staked + ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegate); + let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; + + ensure!(delegation_register.unbonded_balance() >= value, Error::::NotEnoughFunds); + ensure!(delegation_register.payee == *payee, Error::::InvalidRewardDestination); + + T::CoreStaking::bond(who, value, payee) + } + + fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { + ensure!(Self::is_delegate(who), Error::::NotSupported); + return T::CoreStaking::nominate(who, validators); + } + + fn chill(who: &Self::AccountId) -> DispatchResult { + ensure!(Self::is_delegate(who), Error::::NotSupported); + return T::CoreStaking::chill(who); + } + + fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { + let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; + ensure!(delegation_register.unbonded_balance() >= extra, Error::::NotEnoughFunds); + + T::CoreStaking::bond_extra(who, extra) + } + + fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { + let delegation_register = >::get(stash).ok_or(Error::::NotDelegate)?; + ensure!(delegation_register.hold >= value, Error::::NotEnoughFunds); + + T::CoreStaking::unbond(stash, value) + } + + /// Not supported, call [`Delegate::withdraw`] + fn withdraw_unbonded( + _stash: Self::AccountId, + _num_slashing_spans: u32, + ) -> Result { + // FIXME(ank4n): Support withdrawing to self account. + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + /// Not supported, call [`Delegate::withdraw`] + fn withdraw_exact( + _stash: &Self::AccountId, + _amount: Self::Balance, + _num_slashing_spans: u32, + ) -> Result { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + fn desired_validator_count() -> u32 { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::desired_validator_count() + } + + fn election_ongoing() -> bool { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::election_ongoing() + } + + fn force_unstake(_who: Self::AccountId) -> DispatchResult { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + Err(Error::::NotSupported.into()) + } + + fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::is_exposed_in_era(who, era) + } + + fn status(who: &Self::AccountId) -> Result, DispatchError> { + ensure!(Self::is_delegate(who), Error::::NotSupported); + T::CoreStaking::status(who) + } + + fn is_validator(who: &Self::AccountId) -> bool { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + T::CoreStaking::is_validator(who) + } + + fn nominations(who: &Self::AccountId) -> Option> { + T::CoreStaking::nominations(who) + } + + fn slash_reward_fraction() -> Perbill { + T::CoreStaking::slash_reward_fraction() + } + + fn release_all(_who: &Self::AccountId) { + defensive_assert!(false, "not supported for delegated impl of staking interface"); + } + + #[cfg(feature = "runtime-benchmarks")] + fn max_exposure_page_size() -> sp_staking::Page { + T::CoreStaking::max_exposure_page_size() + } + + #[cfg(feature = "runtime-benchmarks")] + fn add_era_stakers( + current_era: &EraIndex, + stash: &Self::AccountId, + exposures: Vec<(Self::AccountId, Self::Balance)>, + ) { + T::CoreStaking::add_era_stakers(current_era, stash, exposures) + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_current_era(era: EraIndex) { + T::CoreStaking::set_current_era(era) + } +} + +impl DelegationInterface for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn delegated_balance(who: &Self::AccountId) -> Self::Balance { + >::get(who) + .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) + } + + fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { + >::get(who).map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) + } + + fn accept_delegations( + who: &Self::AccountId, + reward_destination: &Self::AccountId, + ) -> DispatchResult { + Self::register_as_delegate( + RawOrigin::Signed(who.clone()).into(), + reward_destination.clone(), + ) + } + + /// Transfers funds from current staked account to `proxy_delegator`. Current staked account + /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. + fn migrate_accept_delegations( + new_delegate: &Self::AccountId, + proxy_delegator: &Self::AccountId, + payee: &Self::AccountId, + ) -> DispatchResult { + ensure!(new_delegate != proxy_delegator, Error::::InvalidDelegation); + + // ensure proxy delegator has at least minimum balance to keep the account alive. + ensure!( + T::Currency::reducible_balance( + proxy_delegator, + Preservation::Expendable, + Fortitude::Polite + ) > Zero::zero(), + Error::::NotEnoughFunds + ); + + // ensure staker is a nominator + let status = T::CoreStaking::status(new_delegate)?; + match status { + StakerStatus::Nominator(_) => (), + _ => return Err(Error::::InvalidDelegation.into()), + } + + >::insert(&new_delegate, &proxy_delegator); + let stake = T::CoreStaking::stake(new_delegate)?; + + // unlock funds from staker + T::CoreStaking::release_all(new_delegate); + + // try transferring the staked amount. This should never fail but if it does, it indicates + // bad state and we abort. + T::Currency::transfer(new_delegate, proxy_delegator, stake.total, Preservation::Expendable) + .map_err(|_| Error::::BadState)?; + + // delegate from new delegator to staker. + // todo(ank4n) : inline this fn and propagate payee to core staking.. + Self::accept_delegations(new_delegate, payee)?; + + Self::delegate(proxy_delegator, new_delegate, stake.total)?; + Self::bond_all(new_delegate) + } + + fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { + let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; + register.blocked = true; + >::insert(delegate, register); + + Ok(()) + } + + fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult { + let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; + register.blocked = false; + >::insert(delegate, register); + + Ok(()) + } + + fn kill_delegate(_delegate: &Self::AccountId) -> DispatchResult { + todo!() + } + + fn bond_all(who: &Self::AccountId) -> DispatchResult { + let delegate = >::get(who).ok_or(Error::::NotDelegate)?; + let amount_to_bond = delegate.unbonded_balance(); + + match T::CoreStaking::stake(who) { + // already bonded + Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), + // first bond + Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegate.payee), + } + } + + #[transactional] + fn delegate_withdraw( + delegate: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + num_slashing_spans: u32, + ) -> DispatchResult { + // check how much is already unbonded + let delegation_register = >::get(delegate).ok_or(Error::::NotDelegate)?; + let unbonded_balance = delegation_register.unbonded_balance(); + + if unbonded_balance < value { + // fixme(ank4n) handle killing of stash + let amount_to_withdraw = value.saturating_sub(unbonded_balance); + let _stash_killed: bool = + T::CoreStaking::withdraw_exact(delegate, amount_to_withdraw, num_slashing_spans) + .map_err(|_| Error::::WithdrawFailed)?; + } + + Self::delegation_withdraw(delegator, delegate, value) + } + + fn apply_slash( + delegate: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> DispatchResult { + let mut delegation_register = + >::get(delegate).ok_or(Error::::NotDelegate)?; + let (assigned_delegate, delegate_balance) = + >::get(delegator).ok_or(Error::::NotDelegator)?; + + ensure!(&assigned_delegate == delegate, Error::::NotDelegate); + ensure!(delegate_balance >= value, Error::::NotEnoughFunds); + + let (mut credit, _missing) = + T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); + let actual_slash = credit.peek(); + // remove the slashed amount + delegation_register.pending_slash.saturating_reduce(actual_slash); + >::insert(delegate, delegation_register); + + if let Some(reporter) = maybe_reporter { + let reward_payout: BalanceOf = + T::CoreStaking::slash_reward_fraction() * actual_slash; + let (reporter_reward, rest) = credit.split(reward_payout); + credit = rest; + // fixme(ank4n): handle error + let _ = T::Currency::resolve(&reporter, reporter_reward); + } + + T::OnSlash::on_unbalanced(credit); + Ok(()) + } + + /// Move funds from proxy delegator to actual delegator. + #[transactional] + fn migrate_delegator( + delegate: &Self::AccountId, + new_delegator: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult { + ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); + // make sure new delegator is not an existing delegator or a delegate + ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); + ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); + // ensure we are migrating + let proxy_delegator = + >::get(delegate).ok_or(Error::::NotMigrating)?; + // proxy delegator must exist + let (assigned_delegate, delegate_balance) = + >::get(&proxy_delegator).ok_or(Error::::BadState)?; + ensure!(assigned_delegate == *delegate, Error::::BadState); + + // make sure proxy delegator has enough balance to support this migration. + ensure!(delegate_balance >= value, Error::::NotEnoughFunds); + + // remove delegation of `value` from `proxy_delegator`. + let updated_delegate_balance = delegate_balance.saturating_sub(value); + + // if all funds are migrated out of proxy delegator, clean up. + if updated_delegate_balance == BalanceOf::::zero() { + >::remove(&proxy_delegator); + >::remove(delegate); + } else { + // else update proxy delegator + >::insert(&proxy_delegator, (delegate, updated_delegate_balance)); + } + + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &proxy_delegator, + value, + Precision::BestEffort, + )?; + + defensive_assert!(released == value, "hold should have been released fully"); + + // transfer the withdrawn value to `new_delegator`. + T::Currency::transfer(&proxy_delegator, new_delegator, value, Preservation::Expendable) + .map_err(|_| Error::::BadState)?; + + // add the above removed delegation to `new_delegator`. + >::insert(new_delegator, (delegate, value)); + // hold the funds again in the new delegator account. + T::Currency::hold(&HoldReason::Delegating.into(), &new_delegator, value)?; + + Ok(()) + } + + fn delegate( + delegator: &Self::AccountId, + delegate: &Self::AccountId, + value: Self::Balance, + ) -> DispatchResult { + let delegator_balance = + T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); + ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); + ensure!(delegator_balance >= value, Error::::NotEnoughFunds); + ensure!(delegate != delegator, Error::::InvalidDelegation); + + let mut delegation_register = + >::get(delegate).ok_or(Error::::NotDelegate)?; + ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); + + // A delegate cannot delegate. + if >::contains_key(delegator) { + return Err(Error::::InvalidDelegation.into()) + } + + let new_delegation_amount = + if let Some((current_delegate, current_delegation)) = >::get(delegator) { + ensure!(¤t_delegate == delegate, Error::::InvalidDelegation); + value.saturating_add(current_delegation) + } else { + value + }; + + delegation_register.total_delegated.saturating_accrue(value); + + >::insert(delegator, (delegate, new_delegation_amount)); + >::insert(delegate, delegation_register); + + T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; + + Self::deposit_event(Event::::Delegated { + delegate: delegate.clone(), + delegator: delegator.clone(), + amount: value, + }); + + Ok(()) + } +} + +impl StakingDelegationSupport for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + >::get(who) + .map(|delegate| delegate.delegated_balance()) + .unwrap_or_default() + } + + fn restrict_reward_destination( + who: &Self::AccountId, + reward_destination: Option, + ) -> bool { + let maybe_register = >::get(who); + + if maybe_register.is_none() { + // no restrictions for non delegates. + return false; + } + + // restrict if reward destination is not set + if reward_destination.is_none() { + return true; + } + + let register = maybe_register.expect("checked above; qed"); + let reward_acc = reward_destination.expect("checked above; qed"); + + // restrict if reward account is not what delegate registered. + register.payee != reward_acc + } + + fn is_delegate(who: &Self::AccountId) -> bool { + Self::is_delegate(who) + } + + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + ensure!(Self::is_delegate(who), Error::::NotSupported); + + // delegation register should exist since `who` is a delegate. + let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; + + ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); + ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); + let updated_register = DelegationLedger { hold: amount, ..delegation_register }; + >::insert(who, updated_register); + + Ok(()) + } + + fn report_slash(who: &Self::AccountId, slash: Self::Balance) { + >::mutate(who, |maybe_register| match maybe_register { + Some(register) => register.pending_slash.saturating_accrue(slash), + None => { + defensive!("should not be called on non-delegate"); + }, + }); + } +} diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 2c426dfdec75..f6b4fff758bf 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -57,6 +57,9 @@ mod types; use types::*; +// implementation of public traits. +mod impls; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; @@ -303,506 +306,7 @@ pub mod pallet { } } -impl DelegationInterface for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - fn delegated_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) - } - - fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who).map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) - } - - fn accept_delegations( - who: &Self::AccountId, - reward_destination: &Self::AccountId, - ) -> DispatchResult { - Self::register_as_delegate( - RawOrigin::Signed(who.clone()).into(), - reward_destination.clone(), - ) - } - - /// Transfers funds from current staked account to `proxy_delegator`. Current staked account - /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. - fn migrate_accept_delegations( - new_delegate: &Self::AccountId, - proxy_delegator: &Self::AccountId, - payee: &Self::AccountId, - ) -> DispatchResult { - ensure!(new_delegate != proxy_delegator, Error::::InvalidDelegation); - - // ensure proxy delegator has at least minimum balance to keep the account alive. - ensure!( - T::Currency::reducible_balance( - proxy_delegator, - Preservation::Expendable, - Fortitude::Polite - ) > Zero::zero(), - Error::::NotEnoughFunds - ); - - // ensure staker is a nominator - let status = T::CoreStaking::status(new_delegate)?; - match status { - StakerStatus::Nominator(_) => (), - _ => return Err(Error::::InvalidDelegation.into()), - } - - >::insert(&new_delegate, &proxy_delegator); - let stake = T::CoreStaking::stake(new_delegate)?; - - // unlock funds from staker - T::CoreStaking::release_all(new_delegate); - - // try transferring the staked amount. This should never fail but if it does, it indicates - // bad state and we abort. - T::Currency::transfer(new_delegate, proxy_delegator, stake.total, Preservation::Expendable) - .map_err(|_| Error::::BadState)?; - - // delegate from new delegator to staker. - // todo(ank4n) : inline this fn and propagate payee to core staking.. - Self::accept_delegations(new_delegate, payee)?; - - Self::delegate(proxy_delegator, new_delegate, stake.total)?; - Self::bond_all(new_delegate) - } - - fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { - let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; - register.blocked = true; - >::insert(delegate, register); - - Ok(()) - } - - fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult { - let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; - register.blocked = false; - >::insert(delegate, register); - - Ok(()) - } - - fn kill_delegate(_delegate: &Self::AccountId) -> DispatchResult { - todo!() - } - - fn bond_all(who: &Self::AccountId) -> DispatchResult { - let delegate = >::get(who).ok_or(Error::::NotDelegate)?; - let amount_to_bond = delegate.unbonded_balance(); - - match T::CoreStaking::stake(who) { - // already bonded - Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), - // first bond - Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegate.payee), - } - } - - #[transactional] - fn delegate_withdraw( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - num_slashing_spans: u32, - ) -> DispatchResult { - // check how much is already unbonded - let delegation_register = >::get(delegate).ok_or(Error::::NotDelegate)?; - let unbonded_balance = delegation_register.unbonded_balance(); - - if unbonded_balance < value { - // fixme(ank4n) handle killing of stash - let amount_to_withdraw = value.saturating_sub(unbonded_balance); - let _stash_killed: bool = - T::CoreStaking::withdraw_exact(delegate, amount_to_withdraw, num_slashing_spans) - .map_err(|_| Error::::WithdrawFailed)?; - } - - Self::delegation_withdraw(delegator, delegate, value) - } - - fn apply_slash( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, - ) -> DispatchResult { - let mut delegation_register = - >::get(delegate).ok_or(Error::::NotDelegate)?; - let (assigned_delegate, delegate_balance) = - >::get(delegator).ok_or(Error::::NotDelegator)?; - - ensure!(&assigned_delegate == delegate, Error::::NotDelegate); - ensure!(delegate_balance >= value, Error::::NotEnoughFunds); - - let (mut credit, _missing) = - T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); - let actual_slash = credit.peek(); - // remove the slashed amount - delegation_register.pending_slash.saturating_reduce(actual_slash); - >::insert(delegate, delegation_register); - - if let Some(reporter) = maybe_reporter { - let reward_payout: BalanceOf = - T::CoreStaking::slash_reward_fraction() * actual_slash; - let (reporter_reward, rest) = credit.split(reward_payout); - credit = rest; - // fixme(ank4n): handle error - let _ = T::Currency::resolve(&reporter, reporter_reward); - } - - T::OnSlash::on_unbalanced(credit); - Ok(()) - } - - /// Move funds from proxy delegator to actual delegator. - #[transactional] - fn migrate_delegator( - delegate: &Self::AccountId, - new_delegator: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult { - ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - // make sure new delegator is not an existing delegator or a delegate - ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); - ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); - // ensure we are migrating - let proxy_delegator = - >::get(delegate).ok_or(Error::::NotMigrating)?; - // proxy delegator must exist - let (assigned_delegate, delegate_balance) = - >::get(&proxy_delegator).ok_or(Error::::BadState)?; - ensure!(assigned_delegate == *delegate, Error::::BadState); - - // make sure proxy delegator has enough balance to support this migration. - ensure!(delegate_balance >= value, Error::::NotEnoughFunds); - - // remove delegation of `value` from `proxy_delegator`. - let updated_delegate_balance = delegate_balance.saturating_sub(value); - - // if all funds are migrated out of proxy delegator, clean up. - if updated_delegate_balance == BalanceOf::::zero() { - >::remove(&proxy_delegator); - >::remove(delegate); - } else { - // else update proxy delegator - >::insert(&proxy_delegator, (delegate, updated_delegate_balance)); - } - - let released = T::Currency::release( - &HoldReason::Delegating.into(), - &proxy_delegator, - value, - Precision::BestEffort, - )?; - - defensive_assert!(released == value, "hold should have been released fully"); - - // transfer the withdrawn value to `new_delegator`. - T::Currency::transfer(&proxy_delegator, new_delegator, value, Preservation::Expendable) - .map_err(|_| Error::::BadState)?; - - // add the above removed delegation to `new_delegator`. - >::insert(new_delegator, (delegate, value)); - // hold the funds again in the new delegator account. - T::Currency::hold(&HoldReason::Delegating.into(), &new_delegator, value)?; - - Ok(()) - } - - fn delegate( - delegator: &Self::AccountId, - delegate: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult { - let delegator_balance = - T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); - ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - ensure!(delegator_balance >= value, Error::::NotEnoughFunds); - ensure!(delegate != delegator, Error::::InvalidDelegation); - - let mut delegation_register = - >::get(delegate).ok_or(Error::::NotDelegate)?; - ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); - - // A delegate cannot delegate. - if >::contains_key(delegator) { - return Err(Error::::InvalidDelegation.into()) - } - - let new_delegation_amount = - if let Some((current_delegate, current_delegation)) = >::get(delegator) { - ensure!(¤t_delegate == delegate, Error::::InvalidDelegation); - value.saturating_add(current_delegation) - } else { - value - }; - - delegation_register.total_delegated.saturating_accrue(value); - - >::insert(delegator, (delegate, new_delegation_amount)); - >::insert(delegate, delegation_register); - - T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; - - Self::deposit_event(Event::::Delegated { - delegate: delegate.clone(), - delegator: delegator.clone(), - amount: value, - }); - - Ok(()) - } -} - -impl StakingDelegationSupport for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map(|delegate| delegate.delegated_balance()) - .unwrap_or_default() - } - - fn restrict_reward_destination( - who: &Self::AccountId, - reward_destination: Option, - ) -> bool { - let maybe_register = >::get(who); - - if maybe_register.is_none() { - // no restrictions for non delegates. - return false; - } - - // restrict if reward destination is not set - if reward_destination.is_none() { - return true; - } - - let register = maybe_register.expect("checked above; qed"); - let reward_acc = reward_destination.expect("checked above; qed"); - - // restrict if reward account is not what delegate registered. - register.payee != reward_acc - } - - fn is_delegate(who: &Self::AccountId) -> bool { - Self::is_delegate(who) - } - - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); - - // delegation register should exist since `who` is a delegate. - let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; - - ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); - ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = DelegationLedger { hold: amount, ..delegation_register }; - >::insert(who, updated_register); - - Ok(()) - } - - fn report_slash(who: &Self::AccountId, slash: Self::Balance) { - >::mutate(who, |maybe_register| match maybe_register { - Some(register) => register.pending_slash.saturating_accrue(slash), - None => { - defensive!("should not be called on non-delegate"); - }, - }); - } -} - -/// StakingInterface implementation with delegation support. -/// -/// Only supports Nominators via Delegated Bonds. It is possible for a nominator to migrate and -/// become a `Delegate`. -impl StakingInterface for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - type CurrencyToVote = ::CurrencyToVote; - - fn minimum_nominator_bond() -> Self::Balance { - T::CoreStaking::minimum_nominator_bond() - } - - fn minimum_validator_bond() -> Self::Balance { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - T::CoreStaking::minimum_validator_bond() - } - - fn stash_by_ctrl(_controller: &Self::AccountId) -> Result { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - // ctrl are deprecated, just return err. - Err(Error::::NotSupported.into()) - } - - fn bonding_duration() -> EraIndex { - T::CoreStaking::bonding_duration() - } - - fn current_era() -> EraIndex { - T::CoreStaking::current_era() - } - - fn stake(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegate(who), Error::::NotSupported); - return T::CoreStaking::stake(who); - } - - fn total_stake(who: &Self::AccountId) -> Result { - if Self::is_delegate(who) { - return T::CoreStaking::total_stake(who); - } - - if Self::is_delegator(who) { - let (_, delegation_amount) = - >::get(who).defensive_ok_or(Error::::BadState)?; - return Ok(delegation_amount) - } - - Err(Error::::NotSupported.into()) - } - - fn active_stake(who: &Self::AccountId) -> Result { - T::CoreStaking::active_stake(who) - } - - fn is_unbonding(who: &Self::AccountId) -> Result { - T::CoreStaking::is_unbonding(who) - } - - fn fully_unbond(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); - return T::CoreStaking::fully_unbond(who); - } - - fn bond( - who: &Self::AccountId, - value: Self::Balance, - payee: &Self::AccountId, - ) -> DispatchResult { - // ensure who is not already staked - ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegate); - let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; - - ensure!(delegation_register.unbonded_balance() >= value, Error::::NotEnoughFunds); - ensure!(delegation_register.payee == *payee, Error::::InvalidRewardDestination); - - T::CoreStaking::bond(who, value, payee) - } - - fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); - return T::CoreStaking::nominate(who, validators); - } - - fn chill(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); - return T::CoreStaking::chill(who); - } - - fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { - let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; - ensure!(delegation_register.unbonded_balance() >= extra, Error::::NotEnoughFunds); - - T::CoreStaking::bond_extra(who, extra) - } - - fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { - let delegation_register = >::get(stash).ok_or(Error::::NotDelegate)?; - ensure!(delegation_register.hold >= value, Error::::NotEnoughFunds); - - T::CoreStaking::unbond(stash, value) - } - - /// Not supported, call [`Delegate::withdraw`] - fn withdraw_unbonded( - _stash: Self::AccountId, - _num_slashing_spans: u32, - ) -> Result { - // FIXME(ank4n): Support withdrawing to self account. - defensive_assert!(false, "not supported for delegated impl of staking interface"); - Err(Error::::NotSupported.into()) - } - - /// Not supported, call [`Delegate::withdraw`] - fn withdraw_exact( - _stash: &Self::AccountId, - _amount: Self::Balance, - _num_slashing_spans: u32, - ) -> Result { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - Err(Error::::NotSupported.into()) - } - - fn desired_validator_count() -> u32 { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - T::CoreStaking::desired_validator_count() - } - - fn election_ongoing() -> bool { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - T::CoreStaking::election_ongoing() - } - - fn force_unstake(_who: Self::AccountId) -> DispatchResult { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - Err(Error::::NotSupported.into()) - } - - fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - T::CoreStaking::is_exposed_in_era(who, era) - } - - fn status(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegate(who), Error::::NotSupported); - T::CoreStaking::status(who) - } - - fn is_validator(who: &Self::AccountId) -> bool { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - T::CoreStaking::is_validator(who) - } - fn nominations(who: &Self::AccountId) -> Option> { - T::CoreStaking::nominations(who) - } - - fn slash_reward_fraction() -> Perbill { - T::CoreStaking::slash_reward_fraction() - } - - fn release_all(_who: &Self::AccountId) { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - } - - #[cfg(feature = "runtime-benchmarks")] - fn max_exposure_page_size() -> sp_staking::Page { - T::CoreStaking::max_exposure_page_size() - } - - #[cfg(feature = "runtime-benchmarks")] - fn add_era_stakers( - current_era: &EraIndex, - stash: &Self::AccountId, - exposures: Vec<(Self::AccountId, Self::Balance)>, - ) { - T::CoreStaking::add_era_stakers(current_era, stash, exposures) - } - - #[cfg(feature = "runtime-benchmarks")] - fn set_current_era(era: EraIndex) { - T::CoreStaking::set_current_era(era) - } -} impl Pallet { fn sub_account(account_type: AccountType, delegate_account: T::AccountId) -> T::AccountId { T::PalletId::get().into_sub_account_truncating((account_type, delegate_account.clone())) From 71c9fffdaf30b187b0fa6f1873f7b1021adac5ba Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 18:20:17 +0100 Subject: [PATCH 086/202] add delegation struct --- .../frame/delegated-staking/src/impls.rs | 40 +++++++++++-------- substrate/frame/delegated-staking/src/lib.rs | 19 +++++---- .../frame/delegated-staking/src/types.rs | 30 +++++++++++++- 3 files changed, 61 insertions(+), 28 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 03fbe2437b6e..f6401b746361 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -64,9 +64,9 @@ impl StakingInterface for Pallet { } if Self::is_delegator(who) { - let (_, delegation_amount) = - >::get(who).defensive_ok_or(Error::::BadState)?; - return Ok(delegation_amount) + let delegation = + Delegation::::get(who).defensive_ok_or(Error::::BadState)?; + return Ok(delegation.amount) } Err(Error::::NotSupported.into()) @@ -336,11 +336,11 @@ impl DelegationInterface for Pallet { ) -> DispatchResult { let mut delegation_register = >::get(delegate).ok_or(Error::::NotDelegate)?; - let (assigned_delegate, delegate_balance) = + let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; - ensure!(&assigned_delegate == delegate, Error::::NotDelegate); - ensure!(delegate_balance >= value, Error::::NotEnoughFunds); + ensure!(&delegation.delegate == delegate, Error::::NotDelegate); + ensure!(delegation.amount >= value, Error::::NotEnoughFunds); let (mut credit, _missing) = T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); @@ -377,15 +377,16 @@ impl DelegationInterface for Pallet { let proxy_delegator = >::get(delegate).ok_or(Error::::NotMigrating)?; // proxy delegator must exist - let (assigned_delegate, delegate_balance) = + // let (assigned_delegate, delegate_balance) = + let delegation = >::get(&proxy_delegator).ok_or(Error::::BadState)?; - ensure!(assigned_delegate == *delegate, Error::::BadState); + ensure!(delegation.delegate == *delegate, Error::::BadState); // make sure proxy delegator has enough balance to support this migration. - ensure!(delegate_balance >= value, Error::::NotEnoughFunds); + ensure!(delegation.amount >= value, Error::::NotEnoughFunds); // remove delegation of `value` from `proxy_delegator`. - let updated_delegate_balance = delegate_balance.saturating_sub(value); + let updated_delegate_balance = delegation.amount.saturating_sub(value); // if all funds are migrated out of proxy delegator, clean up. if updated_delegate_balance == BalanceOf::::zero() { @@ -393,7 +394,14 @@ impl DelegationInterface for Pallet { >::remove(delegate); } else { // else update proxy delegator - >::insert(&proxy_delegator, (delegate, updated_delegate_balance)); + >::insert( + &proxy_delegator, + Delegation { + delegate: delegate.clone(), + amount: updated_delegate_balance, + delegator: Some(proxy_delegator.clone()), + } + ); } let released = T::Currency::release( @@ -410,7 +418,7 @@ impl DelegationInterface for Pallet { .map_err(|_| Error::::BadState)?; // add the above removed delegation to `new_delegator`. - >::insert(new_delegator, (delegate, value)); + Delegation::::from(delegate, value).save(new_delegator); // hold the funds again in the new delegator account. T::Currency::hold(&HoldReason::Delegating.into(), &new_delegator, value)?; @@ -438,16 +446,16 @@ impl DelegationInterface for Pallet { } let new_delegation_amount = - if let Some((current_delegate, current_delegation)) = >::get(delegator) { - ensure!(¤t_delegate == delegate, Error::::InvalidDelegation); - value.saturating_add(current_delegation) + if let Some(current_delegation) = >::get(delegator) { + ensure!(¤t_delegation.delegate == delegate, Error::::InvalidDelegation); + value.saturating_add(current_delegation.amount) } else { value }; delegation_register.total_delegated.saturating_accrue(value); - >::insert(delegator, (delegate, new_delegation_amount)); + Delegation::::from(delegate, new_delegation_amount).save(delegator); >::insert(delegate, delegation_register); T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index f6b4fff758bf..3428f9ff1769 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -186,7 +186,7 @@ pub mod pallet { /// want to restrict delegators to delegate only to one account. #[pallet::storage] pub(crate) type Delegators = - CountedStorageMap<_, Twox64Concat, T::AccountId, (T::AccountId, BalanceOf), OptionQuery>; + CountedStorageMap<_, Twox64Concat, T::AccountId, Delegation, OptionQuery>; /// Map of `Delegate` to their Ledger. #[pallet::storage] @@ -352,7 +352,7 @@ impl Pallet { T::Currency::transfer(who, &proxy_delegator, stake.total, Preservation::Protect) .map_err(|_| Error::::BadState)?; - DelegationLedger::::new(&reward_account).save(&who); + let ledger = DelegationLedger::::new(&reward_account).save(&who); // FIXME(ank4n) expose set payee in staking interface. // T::CoreStaking::set_payee(who, reward_account) @@ -378,9 +378,8 @@ impl Pallet { delegate: &T::AccountId, amount: BalanceOf, ) -> DispatchResult { - // let mut delegation_register = - // >::get(delegate).ok_or(Error::::NotDelegate)?; - // ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); + // let mut ledger = >::get(delegate).ok_or(Error::::NotDelegate)?; + // ensure!(!ledger.blocked, Error::::DelegationsBlocked); // // let new_delegation_amount = // if let Some((current_delegate, current_delegation)) = >::get(delegator) { @@ -419,18 +418,18 @@ impl Pallet { delegation_register.total_delegated.saturating_reduce(value); >::insert(delegate, delegation_register); - let (assigned_delegate, delegate_balance) = + let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; // delegator should already be delegating to `delegate` - ensure!(&assigned_delegate == delegate, Error::::NotDelegate); - ensure!(delegate_balance >= value, Error::::NotEnoughFunds); - let updated_delegate_balance = delegate_balance.saturating_sub(value); + ensure!(&delegation.delegate == delegate, Error::::NotDelegate); + ensure!(delegation.amount >= value, Error::::NotEnoughFunds); + let updated_delegate_balance = delegation.amount.saturating_sub(value); // remove delegator if nothing delegated anymore if updated_delegate_balance == BalanceOf::::zero() { >::remove(delegator); } else { - >::insert(delegator, (delegate, updated_delegate_balance)); + >::insert(delegator, Delegation::::from(delegate, updated_delegate_balance)); } let released = T::Currency::release( diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 6de552d23558..df5141f607a1 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -34,10 +34,36 @@ pub(crate) enum AccountType { #[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct Delegation { - // The target of delegation. + /// The target of delegation. pub delegate: T::AccountId, - // The amount delegated. + /// The amount delegated. pub amount: BalanceOf, + /// The `delegator` account who is delegating. + #[codec(skip)] + pub delegator: Option, +} + +impl Delegation { + pub(crate) fn get(delegator: &T::AccountId) -> Option { + >::get(delegator).map(|d| d.with_key(delegator)) + } + + /// consumes self and returns a new copy with the key. + fn with_key(self, key: &T::AccountId) -> Self { + Delegation { delegator: Some(key.clone()), ..self } + } + + pub(crate) fn from(delegate: &T::AccountId, amount: BalanceOf) -> Self { + Delegation { delegate: delegate.clone(), amount, delegator: None } + } + + pub(crate) fn save(self, key: &T::AccountId) -> Self { + let new_val = self.with_key(key); + >::insert(key, &new_val); + + new_val + } + } /// Ledger of all delegations to a `Delegate`. From 5caba0765d98cb6c1b51c45006820be9f113ec04 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 18:54:14 +0100 Subject: [PATCH 087/202] impl delegate --- substrate/frame/delegated-staking/src/lib.rs | 56 +++++++++---------- .../frame/delegated-staking/src/types.rs | 11 ++-- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 3428f9ff1769..715a9db07f2e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -78,8 +78,8 @@ use frame_support::{ }; use sp_runtime::{ - traits::{AccountIdConversion, Zero}, - DispatchResult, Perbill, RuntimeDebug, Saturating, + traits::{AccountIdConversion, Zero, CheckedAdd}, + DispatchResult, Perbill, RuntimeDebug, Saturating, ArithmeticError, }; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, @@ -378,33 +378,31 @@ impl Pallet { delegate: &T::AccountId, amount: BalanceOf, ) -> DispatchResult { - // let mut ledger = >::get(delegate).ok_or(Error::::NotDelegate)?; - // ensure!(!ledger.blocked, Error::::DelegationsBlocked); - // - // let new_delegation_amount = - // if let Some((current_delegate, current_delegation)) = >::get(delegator) { - // ensure!(¤t_delegate == delegate, Error::::InvalidDelegation); - // value.saturating_add(current_delegation) - // } else { - // value - // }; - // - // delegation_register.total_delegated.saturating_accrue(value); - // - // >::insert(delegator, (delegate, new_delegation_amount)); - // >::insert(delegate, delegation_register); - // - // T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; - // - // Self::deposit_event(Event::::Delegated { - // delegate: delegate.clone(), - // delegator: delegator.clone(), - // amount: value, - // }); - // - // Ok(()) - - todo!() + let mut ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; + ensure!(!ledger.blocked, Error::::DelegationsBlocked); + + let new_delegation_amount = + if let Some(existing_delegation) = Delegation::::get(delegator) { + ensure!(&existing_delegation.delegate == delegate, Error::::InvalidDelegation); + existing_delegation.amount.checked_add(&amount).ok_or(ArithmeticError::Overflow)? + } else { + amount + }; + + Delegation::::from(delegate, new_delegation_amount).save(delegator); + + ledger.total_delegated = ledger.total_delegated.checked_add(&new_delegation_amount).ok_or(ArithmeticError::Overflow)?; + ledger.save(delegate); + + T::Currency::hold(&HoldReason::Delegating.into(), &delegator, amount)?; + + Self::deposit_event(Event::::Delegated { + delegate: delegate.clone(), + delegator: delegator.clone(), + amount, + }); + + Ok(()) } fn delegation_withdraw( delegator: &T::AccountId, diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index df5141f607a1..2079ab872313 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -96,7 +96,7 @@ pub struct DelegationLedger { impl DelegationLedger { - pub fn new(reward_destination: &T::AccountId) -> Self { + pub(crate) fn new(reward_destination: &T::AccountId) -> Self { DelegationLedger { payee: reward_destination.clone(), total_delegated: Zero::zero(), @@ -112,12 +112,12 @@ impl DelegationLedger { DelegationLedger { delegate: Some(key.clone()), ..self } } - fn get(key: &T::AccountId) -> Option { + pub(crate) fn get(key: &T::AccountId) -> Option { >::get(key).map(|d| d.with_key(key)) } /// Balance that is stakeable. - pub fn delegated_balance(&self) -> BalanceOf { + pub(crate) fn delegated_balance(&self) -> BalanceOf { // do not allow to stake more than unapplied slash self.total_delegated.saturating_sub(self.pending_slash) } @@ -125,11 +125,12 @@ impl DelegationLedger { /// Balance that is delegated but not bonded. /// /// Can be funds that are unbonded but not withdrawn. - pub fn unbonded_balance(&self) -> BalanceOf { + pub(crate) fn unbonded_balance(&self) -> BalanceOf { + // fixme(ank4n) Remove hold and get balance from removing withdrawal_unclaimed. self.total_delegated.saturating_sub(self.hold) } - pub fn save(self, key: &T::AccountId) -> Self { + pub(crate) fn save(self, key: &T::AccountId) -> Self { let new_val = self.with_key(key); >::insert(key, &new_val); From 0ec296576de312e79e67fc0f511bc9830861ae9e Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 18:58:36 +0100 Subject: [PATCH 088/202] remove the key --- .../frame/delegated-staking/src/impls.rs | 1 - .../frame/delegated-staking/src/types.rs | 36 ++++--------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index f6401b746361..3fd38f23575f 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -399,7 +399,6 @@ impl DelegationInterface for Pallet { Delegation { delegate: delegate.clone(), amount: updated_delegate_balance, - delegator: Some(proxy_delegator.clone()), } ); } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 2079ab872313..303c91b42dbb 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -38,30 +38,19 @@ pub struct Delegation { pub delegate: T::AccountId, /// The amount delegated. pub amount: BalanceOf, - /// The `delegator` account who is delegating. - #[codec(skip)] - pub delegator: Option, } impl Delegation { pub(crate) fn get(delegator: &T::AccountId) -> Option { - >::get(delegator).map(|d| d.with_key(delegator)) - } - - /// consumes self and returns a new copy with the key. - fn with_key(self, key: &T::AccountId) -> Self { - Delegation { delegator: Some(key.clone()), ..self } + >::get(delegator) } pub(crate) fn from(delegate: &T::AccountId, amount: BalanceOf) -> Self { - Delegation { delegate: delegate.clone(), amount, delegator: None } + Delegation { delegate: delegate.clone(), amount } } - pub(crate) fn save(self, key: &T::AccountId) -> Self { - let new_val = self.with_key(key); - >::insert(key, &new_val); - - new_val + pub(crate) fn save(self, key: &T::AccountId) { + >::insert(key, self) } } @@ -88,9 +77,6 @@ pub struct DelegationLedger { pub pending_slash: BalanceOf, /// Whether this `delegate` is blocked from receiving new delegations. pub blocked: bool, - /// The `delegate` account associated with the ledger. - #[codec(skip)] - pub delegate: Option, } @@ -103,17 +89,12 @@ impl DelegationLedger { hold: Zero::zero(), pending_slash: Zero::zero(), blocked: false, - delegate: None, } } - /// consumes self and returns a new copy with the key. - fn with_key(self, key: &T::AccountId) -> Self { - DelegationLedger { delegate: Some(key.clone()), ..self } - } pub(crate) fn get(key: &T::AccountId) -> Option { - >::get(key).map(|d| d.with_key(key)) + >::get(key) } /// Balance that is stakeable. @@ -130,10 +111,7 @@ impl DelegationLedger { self.total_delegated.saturating_sub(self.hold) } - pub(crate) fn save(self, key: &T::AccountId) -> Self { - let new_val = self.with_key(key); - >::insert(key, &new_val); - - new_val + pub(crate) fn save(self, key: &T::AccountId) { + >::insert(key, self) } } From 4a609024af0437a972592763d809535adfd9cbfd Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 20:13:28 +0100 Subject: [PATCH 089/202] fix test --- .../frame/delegated-staking/src/impls.rs | 45 +++---------------- substrate/frame/delegated-staking/src/lib.rs | 31 +++++++------ .../frame/delegated-staking/src/tests.rs | 9 ++-- 3 files changed, 26 insertions(+), 59 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 3fd38f23575f..6c4b447c45fb 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -232,46 +232,13 @@ impl DelegationInterface for Pallet { /// Transfers funds from current staked account to `proxy_delegator`. Current staked account /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. fn migrate_accept_delegations( - new_delegate: &Self::AccountId, - proxy_delegator: &Self::AccountId, - payee: &Self::AccountId, + who: &Self::AccountId, + _proxy_delegator: &Self::AccountId, + reward_destination: &Self::AccountId, ) -> DispatchResult { - ensure!(new_delegate != proxy_delegator, Error::::InvalidDelegation); - - // ensure proxy delegator has at least minimum balance to keep the account alive. - ensure!( - T::Currency::reducible_balance( - proxy_delegator, - Preservation::Expendable, - Fortitude::Polite - ) > Zero::zero(), - Error::::NotEnoughFunds - ); - - // ensure staker is a nominator - let status = T::CoreStaking::status(new_delegate)?; - match status { - StakerStatus::Nominator(_) => (), - _ => return Err(Error::::InvalidDelegation.into()), - } - - >::insert(&new_delegate, &proxy_delegator); - let stake = T::CoreStaking::stake(new_delegate)?; - - // unlock funds from staker - T::CoreStaking::release_all(new_delegate); - - // try transferring the staked amount. This should never fail but if it does, it indicates - // bad state and we abort. - T::Currency::transfer(new_delegate, proxy_delegator, stake.total, Preservation::Expendable) - .map_err(|_| Error::::BadState)?; - - // delegate from new delegator to staker. - // todo(ank4n) : inline this fn and propagate payee to core staking.. - Self::accept_delegations(new_delegate, payee)?; - - Self::delegate(proxy_delegator, new_delegate, stake.total)?; - Self::bond_all(new_delegate) + Self::migrate_to_delegate(RawOrigin::Signed(who.clone()).into(), + reward_destination.clone() + ) } fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 715a9db07f2e..962e3a649d3b 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -215,15 +215,15 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - // They cannot be already a direct staker in the staking pallet. - ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); - // Existing `delegate` cannot register again. ensure!(!Self::is_delegate(&who), Error::::NotAllowed); // A delegator cannot become a `delegate`. ensure!(!Self::is_delegator(&who), Error::::NotAllowed); + // They cannot be already a direct staker in the staking pallet. + ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); + // Reward account cannot be same as `delegate` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); @@ -242,6 +242,9 @@ pub mod pallet { reward_account: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; + // Ensure is not already a delegate. + ensure!(!Self::is_delegate(&who), Error::::NotAllowed); + ensure!(Self::is_direct_nominator(&who), Error::::NotAllowed); // Reward account cannot be same as `delegate` account. @@ -308,7 +311,7 @@ pub mod pallet { impl Pallet { - fn sub_account(account_type: AccountType, delegate_account: T::AccountId) -> T::AccountId { + pub(crate) fn sub_account(account_type: AccountType, delegate_account: T::AccountId) -> T::AccountId { T::PalletId::get().into_sub_account_truncating((account_type, delegate_account.clone())) } @@ -320,10 +323,6 @@ impl Pallet { >::contains_key(who) } - fn is_migrating(delegate: &T::AccountId) -> bool { - >::contains_key(delegate) - } - /// Returns true if who is not already staking. fn not_direct_staker(who: &T::AccountId) -> bool { T::CoreStaking::status(&who).is_err() @@ -337,22 +336,26 @@ impl Pallet { } fn do_migrate_to_delegate(who: &T::AccountId, reward_account: &T::AccountId) -> DispatchResult { + // We create a proxy delegator that will keep all the delegation funds until funds are + // transferred to actual delegator. + let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, who.clone()); + + // Transfer minimum balance to proxy delegator. + T::Currency::transfer(who, &proxy_delegator, T::Currency::minimum_balance(), Preservation::Protect) + .map_err(|_| Error::::NotEnoughFunds)?; + // Get current stake let stake = T::CoreStaking::stake(who)?; // release funds from core staking. T::CoreStaking::release_all(who); - // We create a proxy delegator that will keep all the delegation funds until funds are - // transferred to actual delegator. - let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, who.clone()); - // transferring just released staked amount. This should never fail but if it does, it // indicates bad state and we abort. T::Currency::transfer(who, &proxy_delegator, stake.total, Preservation::Protect) .map_err(|_| Error::::BadState)?; - let ledger = DelegationLedger::::new(&reward_account).save(&who); + DelegationLedger::::new(&reward_account).save(&who); // FIXME(ank4n) expose set payee in staking interface. // T::CoreStaking::set_payee(who, reward_account) @@ -394,7 +397,7 @@ impl Pallet { ledger.total_delegated = ledger.total_delegated.checked_add(&new_delegation_amount).ok_or(ArithmeticError::Overflow)?; ledger.save(delegate); - T::Currency::hold(&HoldReason::Delegating.into(), &delegator, amount)?; + T::Currency::hold(&HoldReason::Delegating.into(), delegator, amount)?; Self::deposit_event(Event::::Delegated { delegate: delegate.clone(), diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 889d9601564b..ce66ddbb3ba8 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -448,11 +448,9 @@ mod integration { // to migrate, nominator needs to set an account as a proxy delegator where staked funds // will be moved and delegated back to this old nominator account. This should be funded // with at least ED. - let proxy_delegator = 202; - fund(&proxy_delegator, ExistentialDeposit::get()); + let proxy_delegator = DelegatedStaking::sub_account(AccountType::ProxyDelegator, 200); assert_ok!(DelegatedStaking::migrate_accept_delegations(&200, &proxy_delegator, &201)); - assert!(DelegatedStaking::is_migrating(&200)); // verify all went well let mut expected_proxy_delegated_amount = staked_amount; @@ -460,7 +458,8 @@ mod integration { Balances::balance_on_hold(&HoldReason::Delegating.into(), &proxy_delegator), expected_proxy_delegated_amount ); - assert_eq!(Balances::free_balance(200), 5000 - staked_amount); + // ED + stake amount is transferred from delegate to proxy delegator account. + assert_eq!(Balances::free_balance(200), 5000 - staked_amount - ExistentialDeposit::get()); assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); @@ -489,8 +488,6 @@ mod integration { assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); } - assert!(!DelegatedStaking::is_migrating(&200)); - // cannot use migrate delegator anymore assert_noop!( DelegatedStaking::migrate_delegator(&200, &305, 1), From 6eb298cf556d1e01da25b437afa0ec841f3b055f Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 21:10:17 +0100 Subject: [PATCH 090/202] fix unclaim withdraw to be a field and not sub account --- substrate/frame/delegated-staking/src/lib.rs | 89 ++++++++++-- .../frame/delegated-staking/src/types.rs | 136 +++++++++--------- 2 files changed, 144 insertions(+), 81 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 962e3a649d3b..e1eb556a9256 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -44,6 +44,16 @@ //! to apply slash. It is `delegate`'s responsibility to apply slashes for each delegator, one at a //! time. Staking pallet ensures the pending slash never exceeds staked amount and would freeze //! further withdraws until pending slashes are applied. +//! +//! +//! ### Note about minimum balance +//! +//! When registering as `delegate`, a sub account for unclaimed withdrawals are created and funded +//! with `ExistentialDeposit`. If the `delegate` is migrating from being a `Nominator`, another +//! sub account is created for managing funds of delegators who have not yet migrated their funds +//! to won account. These accounts are funded by the `Delegate` and it should have enough free +//! balance to cover these. When these accounts are not killed (not needed anymore), the funds are +//! returned to the `Delegate`. #[cfg(test)] mod mock; @@ -71,15 +81,15 @@ use frame_support::{ Balanced, Inspect as FunInspect, Mutate as FunMutate, }, tokens::{fungible::Credit, Fortitude, Precision, Preservation}, - DefensiveOption, Imbalance, OnUnbalanced, + Defensive, DefensiveOption, Imbalance, OnUnbalanced, }, transactional, weights::Weight, }; use sp_runtime::{ - traits::{AccountIdConversion, Zero, CheckedAdd}, - DispatchResult, Perbill, RuntimeDebug, Saturating, ArithmeticError, + traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, + ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, @@ -266,7 +276,39 @@ pub mod pallet { amount: BalanceOf, num_slashing_spans: u32, ) -> DispatchResult { - todo!() + let who = ensure_signed(origin)?; + let mut ledger = DelegationLedger::::get(&who).ok_or(Error::::NotDelegate)?; + + // if we do not already have enough funds to be claimed, try withdraw some more. + if ledger.unclaimed_withdrawals < amount { + let pre_total = + T::CoreStaking::stake(&who).defensive()?.total; + // fixme(ank4n) handle killing of stash + let _stash_killed: bool = + T::CoreStaking::withdraw_unbonded(who.clone(), num_slashing_spans) + .map_err(|_| Error::::WithdrawFailed)?; + + let post_total = + T::CoreStaking::stake(&who).defensive()?.total; + + let new_withdrawn = + post_total.checked_sub(&pre_total).defensive_ok_or(Error::::BadState)?; + + ledger.unclaimed_withdrawals = ledger + .unclaimed_withdrawals + .checked_add(&new_withdrawn) + .ok_or(ArithmeticError::Overflow)?; + } + + + ensure!( + ledger.unclaimed_withdrawals > amount, + Error::::NotEnoughFunds + ); + + ledger.save(&who); + + Self::delegation_withdraw(&delegator, &who, amount) } /// Migrate delegated fund. @@ -309,9 +351,11 @@ pub mod pallet { } } - impl Pallet { - pub(crate) fn sub_account(account_type: AccountType, delegate_account: T::AccountId) -> T::AccountId { + pub(crate) fn sub_account( + account_type: AccountType, + delegate_account: T::AccountId, + ) -> T::AccountId { T::PalletId::get().into_sub_account_truncating((account_type, delegate_account.clone())) } @@ -341,8 +385,13 @@ impl Pallet { let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, who.clone()); // Transfer minimum balance to proxy delegator. - T::Currency::transfer(who, &proxy_delegator, T::Currency::minimum_balance(), Preservation::Protect) - .map_err(|_| Error::::NotEnoughFunds)?; + T::Currency::transfer( + who, + &proxy_delegator, + T::Currency::minimum_balance(), + Preservation::Protect, + ) + .map_err(|_| Error::::NotEnoughFunds)?; // Get current stake let stake = T::CoreStaking::stake(who)?; @@ -387,14 +436,20 @@ impl Pallet { let new_delegation_amount = if let Some(existing_delegation) = Delegation::::get(delegator) { ensure!(&existing_delegation.delegate == delegate, Error::::InvalidDelegation); - existing_delegation.amount.checked_add(&amount).ok_or(ArithmeticError::Overflow)? + existing_delegation + .amount + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)? } else { amount }; Delegation::::from(delegate, new_delegation_amount).save(delegator); - ledger.total_delegated = ledger.total_delegated.checked_add(&new_delegation_amount).ok_or(ArithmeticError::Overflow)?; + ledger.total_delegated = ledger + .total_delegated + .checked_add(&new_delegation_amount) + .ok_or(ArithmeticError::Overflow)?; ledger.save(delegate); T::Currency::hold(&HoldReason::Delegating.into(), delegator, amount)?; @@ -407,6 +462,12 @@ impl Pallet { Ok(()) } + + fn do_release(delegate: &T::AccountId, delegator: &T::AccountId, value: BalanceOf) { + // let mut ledger = + // DelegationLedger::::get(delegate).defensive_ok_or(Error::::NotDelegate)?; + } + // FIXME(ank4n): remove this fn delegation_withdraw( delegator: &T::AccountId, delegate: &T::AccountId, @@ -419,8 +480,7 @@ impl Pallet { delegation_register.total_delegated.saturating_reduce(value); >::insert(delegate, delegation_register); - let delegation = - >::get(delegator).ok_or(Error::::NotDelegator)?; + let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; // delegator should already be delegating to `delegate` ensure!(&delegation.delegate == delegate, Error::::NotDelegate); ensure!(delegation.amount >= value, Error::::NotEnoughFunds); @@ -430,7 +490,10 @@ impl Pallet { if updated_delegate_balance == BalanceOf::::zero() { >::remove(delegator); } else { - >::insert(delegator, Delegation::::from(delegate, updated_delegate_balance)); + >::insert( + delegator, + Delegation::::from(delegate, updated_delegate_balance), + ); } let released = T::Currency::release( diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 303c91b42dbb..6143abded59e 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -23,36 +23,35 @@ use super::*; /// The type of pot account being created. #[derive(Encode, Decode)] pub(crate) enum AccountType { - /// Funds that are withdrawn from the staking ledger but not claimed by the `delegator` yet. - UnclaimedWithdrawal, - /// A proxy delegator account created for a nominator who migrated to a `delegate` account. - /// - /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. - ProxyDelegator, + /// Funds that are withdrawn from the staking ledger but not claimed by the `delegator` yet. + UnclaimedWithdrawal, + /// A proxy delegator account created for a nominator who migrated to a `delegate` account. + /// + /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. + ProxyDelegator, } #[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct Delegation { - /// The target of delegation. - pub delegate: T::AccountId, - /// The amount delegated. - pub amount: BalanceOf, + /// The target of delegation. + pub delegate: T::AccountId, + /// The amount delegated. + pub amount: BalanceOf, } impl Delegation { - pub(crate) fn get(delegator: &T::AccountId) -> Option { - >::get(delegator) - } + pub(crate) fn get(delegator: &T::AccountId) -> Option { + >::get(delegator) + } - pub(crate) fn from(delegate: &T::AccountId, amount: BalanceOf) -> Self { - Delegation { delegate: delegate.clone(), amount } - } - - pub(crate) fn save(self, key: &T::AccountId) { - >::insert(key, self) - } + pub(crate) fn from(delegate: &T::AccountId, amount: BalanceOf) -> Self { + Delegation { delegate: delegate.clone(), amount } + } + pub(crate) fn save(self, key: &T::AccountId) { + >::insert(key, self) + } } /// Ledger of all delegations to a `Delegate`. @@ -63,55 +62,56 @@ impl Delegation { #[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct DelegationLedger { - /// Where the reward should be paid out. - pub payee: T::AccountId, - /// Sum of all delegated funds to this `delegate`. - #[codec(compact)] - pub total_delegated: BalanceOf, - /// Amount that is bonded and held. - // FIXME(ank4n) (can we remove it) - #[codec(compact)] - pub hold: BalanceOf, - /// Slashes that are not yet applied. - #[codec(compact)] - pub pending_slash: BalanceOf, - /// Whether this `delegate` is blocked from receiving new delegations. - pub blocked: bool, + /// Where the reward should be paid out. + pub payee: T::AccountId, + /// Sum of all delegated funds to this `delegate`. + #[codec(compact)] + pub total_delegated: BalanceOf, + /// Amount that is bonded and held. + // FIXME(ank4n) (can we remove it) + #[codec(compact)] + pub hold: BalanceOf, + /// Funds that are withdrawn from core staking but not released to delegator/s. + #[codec(compact)] + pub unclaimed_withdrawals: BalanceOf, + /// Slashes that are not yet applied. + #[codec(compact)] + pub pending_slash: BalanceOf, + /// Whether this `delegate` is blocked from receiving new delegations. + pub blocked: bool, } - - impl DelegationLedger { - pub(crate) fn new(reward_destination: &T::AccountId) -> Self { - DelegationLedger { - payee: reward_destination.clone(), - total_delegated: Zero::zero(), - hold: Zero::zero(), - pending_slash: Zero::zero(), - blocked: false, - } - } - - - pub(crate) fn get(key: &T::AccountId) -> Option { - >::get(key) - } - - /// Balance that is stakeable. - pub(crate) fn delegated_balance(&self) -> BalanceOf { - // do not allow to stake more than unapplied slash - self.total_delegated.saturating_sub(self.pending_slash) - } - - /// Balance that is delegated but not bonded. - /// - /// Can be funds that are unbonded but not withdrawn. - pub(crate) fn unbonded_balance(&self) -> BalanceOf { - // fixme(ank4n) Remove hold and get balance from removing withdrawal_unclaimed. - self.total_delegated.saturating_sub(self.hold) - } - - pub(crate) fn save(self, key: &T::AccountId) { - >::insert(key, self) - } + pub(crate) fn new(reward_destination: &T::AccountId) -> Self { + DelegationLedger { + payee: reward_destination.clone(), + total_delegated: Zero::zero(), + hold: Zero::zero(), + unclaimed_withdrawals: Zero::zero(), + pending_slash: Zero::zero(), + blocked: false, + } + } + + pub(crate) fn get(key: &T::AccountId) -> Option { + >::get(key) + } + + /// Balance that is stakeable. + pub(crate) fn delegated_balance(&self) -> BalanceOf { + // do not allow to stake more than unapplied slash + self.total_delegated.saturating_sub(self.pending_slash) + } + + /// Balance that is delegated but not bonded. + /// + /// Can be funds that are unbonded but not withdrawn. + pub(crate) fn unbonded_balance(&self) -> BalanceOf { + // fixme(ank4n) Remove hold and get balance from removing withdrawal_unclaimed. + self.total_delegated.saturating_sub(self.hold) + } + + pub(crate) fn save(self, key: &T::AccountId) { + >::insert(key, self) + } } From ddb79638a1b62fde6c6b8088245124117f262f99 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 21:52:28 +0100 Subject: [PATCH 091/202] impl release --- substrate/frame/delegated-staking/src/lib.rs | 117 ++++++++++++------ .../frame/delegated-staking/src/types.rs | 2 +- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index e1eb556a9256..2b73ba7d3350 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -277,38 +277,7 @@ pub mod pallet { num_slashing_spans: u32, ) -> DispatchResult { let who = ensure_signed(origin)?; - let mut ledger = DelegationLedger::::get(&who).ok_or(Error::::NotDelegate)?; - - // if we do not already have enough funds to be claimed, try withdraw some more. - if ledger.unclaimed_withdrawals < amount { - let pre_total = - T::CoreStaking::stake(&who).defensive()?.total; - // fixme(ank4n) handle killing of stash - let _stash_killed: bool = - T::CoreStaking::withdraw_unbonded(who.clone(), num_slashing_spans) - .map_err(|_| Error::::WithdrawFailed)?; - - let post_total = - T::CoreStaking::stake(&who).defensive()?.total; - - let new_withdrawn = - post_total.checked_sub(&pre_total).defensive_ok_or(Error::::BadState)?; - - ledger.unclaimed_withdrawals = ledger - .unclaimed_withdrawals - .checked_add(&new_withdrawn) - .ok_or(ArithmeticError::Overflow)?; - } - - - ensure!( - ledger.unclaimed_withdrawals > amount, - Error::::NotEnoughFunds - ); - - ledger.save(&who); - - Self::delegation_withdraw(&delegator, &who, amount) + Self::do_release(&who, &delegator, amount, num_slashing_spans) } /// Migrate delegated fund. @@ -463,9 +432,87 @@ impl Pallet { Ok(()) } - fn do_release(delegate: &T::AccountId, delegator: &T::AccountId, value: BalanceOf) { - // let mut ledger = - // DelegationLedger::::get(delegate).defensive_ok_or(Error::::NotDelegate)?; + fn do_release( + who: &T::AccountId, + delegator: &T::AccountId, + amount: BalanceOf, + num_slashing_spans: u32, + ) -> DispatchResult { + let mut ledger = DelegationLedger::::get(who).ok_or(Error::::NotDelegate)?; + let mut delegation = Delegation::::get(delegator).ok_or(Error::::NotDelegator)?; + + // make sure delegation to be released is sound. + ensure!(&delegation.delegate == who, Error::::NotDelegate); + ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); + + // if we do not already have enough funds to be claimed, try withdraw some more. + if ledger.unclaimed_withdrawals < amount { + ledger = Self::withdraw_unbounded(who, num_slashing_spans)?; + } + + // if we still do not have enough funds to release, abort. + ensure!(ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); + + // book keep into ledger + ledger.total_delegated = ledger.total_delegated.checked_sub(&amount).defensive_ok_or(ArithmeticError::Overflow)?; + ledger.unclaimed_withdrawals = + ledger.unclaimed_withdrawals.checked_sub(&amount).defensive_ok_or(ArithmeticError::Overflow)?; + ledger.save(who); + + // book keep delegation + delegation.amount = delegation.amount.checked_sub(&amount).defensive_ok_or(ArithmeticError::Overflow)?; + + // remove delegator if nothing delegated anymore + if delegation.amount == BalanceOf::::zero() { + >::remove(delegator); + } else { + delegation.save(delegator); + } + + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &delegator, + amount, + Precision::BestEffort, + )?; + + defensive_assert!(released == amount, "hold should have been released fully"); + + Self::deposit_event(Event::::Withdrawn { + delegate: who.clone(), + delegator: delegator.clone(), + amount, + }); + + Ok(()) + } + + fn withdraw_unbounded( + delegate: &T::AccountId, + num_slashing_spans: u32, + ) -> Result, DispatchError> { + let mut ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; + + let pre_total = T::CoreStaking::stake(delegate).defensive()?.total; + + // fixme(ank4n) handle killing of stash + let _stash_killed: bool = + T::CoreStaking::withdraw_unbonded(delegate.clone(), num_slashing_spans) + .map_err(|_| Error::::WithdrawFailed)?; + + let post_total = T::CoreStaking::stake(delegate).defensive()?.total; + + let new_withdrawn = + post_total.checked_sub(&pre_total).defensive_ok_or(Error::::BadState)?; + + ledger.unclaimed_withdrawals = ledger + .unclaimed_withdrawals + .checked_add(&new_withdrawn) + .ok_or(ArithmeticError::Overflow)?; + + ledger.clone().save(delegate); + + Ok(ledger) } // FIXME(ank4n): remove this fn delegation_withdraw( diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 6143abded59e..14121df2de70 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -59,7 +59,7 @@ impl Delegation { /// This keeps track of the active balance of the `delegate` that is made up from the funds that are /// currently delegated to this `delegate`. It also tracks the pending slashes yet to be applied /// among other things. -#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[derive(Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct DelegationLedger { /// Where the reward should be paid out. From e6656a6122e74883d673e37e9a73d80dfc5a2244 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 21:55:42 +0100 Subject: [PATCH 092/202] cleanup --- .../frame/delegated-staking/src/impls.rs | 151 ++++++++---------- substrate/frame/delegated-staking/src/lib.rs | 45 ------ 2 files changed, 68 insertions(+), 128 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 6c4b447c45fb..fb8a37db6ffe 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -64,8 +64,7 @@ impl StakingInterface for Pallet { } if Self::is_delegator(who) { - let delegation = - Delegation::::get(who).defensive_ok_or(Error::::BadState)?; + let delegation = Delegation::::get(who).defensive_ok_or(Error::::BadState)?; return Ok(delegation.amount) } @@ -236,9 +235,7 @@ impl DelegationInterface for Pallet { _proxy_delegator: &Self::AccountId, reward_destination: &Self::AccountId, ) -> DispatchResult { - Self::migrate_to_delegate(RawOrigin::Signed(who.clone()).into(), - reward_destination.clone() - ) + Self::migrate_to_delegate(RawOrigin::Signed(who.clone()).into(), reward_destination.clone()) } fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { @@ -280,19 +277,12 @@ impl DelegationInterface for Pallet { value: Self::Balance, num_slashing_spans: u32, ) -> DispatchResult { - // check how much is already unbonded - let delegation_register = >::get(delegate).ok_or(Error::::NotDelegate)?; - let unbonded_balance = delegation_register.unbonded_balance(); - - if unbonded_balance < value { - // fixme(ank4n) handle killing of stash - let amount_to_withdraw = value.saturating_sub(unbonded_balance); - let _stash_killed: bool = - T::CoreStaking::withdraw_exact(delegate, amount_to_withdraw, num_slashing_spans) - .map_err(|_| Error::::WithdrawFailed)?; - } - - Self::delegation_withdraw(delegator, delegate, value) + Self::release( + RawOrigin::Signed(delegate.clone()).into(), + delegator.clone(), + value, + num_slashing_spans, + ) } fn apply_slash( @@ -303,8 +293,7 @@ impl DelegationInterface for Pallet { ) -> DispatchResult { let mut delegation_register = >::get(delegate).ok_or(Error::::NotDelegate)?; - let delegation = - >::get(delegator).ok_or(Error::::NotDelegator)?; + let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; ensure!(&delegation.delegate == delegate, Error::::NotDelegate); ensure!(delegation.amount >= value, Error::::NotEnoughFunds); @@ -345,8 +334,7 @@ impl DelegationInterface for Pallet { >::get(delegate).ok_or(Error::::NotMigrating)?; // proxy delegator must exist // let (assigned_delegate, delegate_balance) = - let delegation = - >::get(&proxy_delegator).ok_or(Error::::BadState)?; + let delegation = >::get(&proxy_delegator).ok_or(Error::::BadState)?; ensure!(delegation.delegate == *delegate, Error::::BadState); // make sure proxy delegator has enough balance to support this migration. @@ -363,10 +351,7 @@ impl DelegationInterface for Pallet { // else update proxy delegator >::insert( &proxy_delegator, - Delegation { - delegate: delegate.clone(), - amount: updated_delegate_balance, - } + Delegation { delegate: delegate.clone(), amount: updated_delegate_balance }, ); } @@ -437,61 +422,61 @@ impl DelegationInterface for Pallet { } impl StakingDelegationSupport for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map(|delegate| delegate.delegated_balance()) - .unwrap_or_default() - } - - fn restrict_reward_destination( - who: &Self::AccountId, - reward_destination: Option, - ) -> bool { - let maybe_register = >::get(who); - - if maybe_register.is_none() { - // no restrictions for non delegates. - return false; - } - - // restrict if reward destination is not set - if reward_destination.is_none() { - return true; - } - - let register = maybe_register.expect("checked above; qed"); - let reward_acc = reward_destination.expect("checked above; qed"); - - // restrict if reward account is not what delegate registered. - register.payee != reward_acc - } - - fn is_delegate(who: &Self::AccountId) -> bool { - Self::is_delegate(who) - } - - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); - - // delegation register should exist since `who` is a delegate. - let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; - - ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); - ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = DelegationLedger { hold: amount, ..delegation_register }; - >::insert(who, updated_register); - - Ok(()) - } - - fn report_slash(who: &Self::AccountId, slash: Self::Balance) { - >::mutate(who, |maybe_register| match maybe_register { - Some(register) => register.pending_slash.saturating_accrue(slash), - None => { - defensive!("should not be called on non-delegate"); - }, - }); - } + type Balance = BalanceOf; + type AccountId = T::AccountId; + fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { + >::get(who) + .map(|delegate| delegate.delegated_balance()) + .unwrap_or_default() + } + + fn restrict_reward_destination( + who: &Self::AccountId, + reward_destination: Option, + ) -> bool { + let maybe_register = >::get(who); + + if maybe_register.is_none() { + // no restrictions for non delegates. + return false; + } + + // restrict if reward destination is not set + if reward_destination.is_none() { + return true; + } + + let register = maybe_register.expect("checked above; qed"); + let reward_acc = reward_destination.expect("checked above; qed"); + + // restrict if reward account is not what delegate registered. + register.payee != reward_acc + } + + fn is_delegate(who: &Self::AccountId) -> bool { + Self::is_delegate(who) + } + + fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + ensure!(Self::is_delegate(who), Error::::NotSupported); + + // delegation register should exist since `who` is a delegate. + let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; + + ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); + ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); + let updated_register = DelegationLedger { hold: amount, ..delegation_register }; + >::insert(who, updated_register); + + Ok(()) + } + + fn report_slash(who: &Self::AccountId, slash: Self::Balance) { + >::mutate(who, |maybe_register| match maybe_register { + Some(register) => register.pending_slash.saturating_accrue(slash), + None => { + defensive!("should not be called on non-delegate"); + }, + }); + } } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 2b73ba7d3350..1e31e5c34445 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -514,51 +514,6 @@ impl Pallet { Ok(ledger) } - // FIXME(ank4n): remove this - fn delegation_withdraw( - delegator: &T::AccountId, - delegate: &T::AccountId, - value: BalanceOf, - ) -> DispatchResult { - let mut delegation_register = - >::get(delegate).ok_or(Error::::NotDelegate)?; - ensure!(delegation_register.unbonded_balance() >= value, Error::::BadState); - - delegation_register.total_delegated.saturating_reduce(value); - >::insert(delegate, delegation_register); - - let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; - // delegator should already be delegating to `delegate` - ensure!(&delegation.delegate == delegate, Error::::NotDelegate); - ensure!(delegation.amount >= value, Error::::NotEnoughFunds); - let updated_delegate_balance = delegation.amount.saturating_sub(value); - - // remove delegator if nothing delegated anymore - if updated_delegate_balance == BalanceOf::::zero() { - >::remove(delegator); - } else { - >::insert( - delegator, - Delegation::::from(delegate, updated_delegate_balance), - ); - } - - let released = T::Currency::release( - &HoldReason::Delegating.into(), - &delegator, - value, - Precision::BestEffort, - )?; - - defensive_assert!(released == value, "hold should have been released fully"); - Self::deposit_event(Event::::Withdrawn { - delegate: delegate.clone(), - delegator: delegator.clone(), - amount: value, - }); - - Ok(()) - } } #[cfg(any(test, feature = "try-runtime"))] From 8002ef0c149abfd83ff710f4c67e83f085b3a289 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 21:56:03 +0100 Subject: [PATCH 093/202] cargo fmt --- substrate/frame/delegated-staking/src/lib.rs | 16 +++++++++++---- .../frame/delegated-staking/src/tests.rs | 5 ++++- substrate/frame/nomination-pools/src/lib.rs | 20 +++++++++++-------- substrate/frame/staking/src/lib.rs | 1 - 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1e31e5c34445..98909b0e39fe 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -454,13 +454,21 @@ impl Pallet { ensure!(ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); // book keep into ledger - ledger.total_delegated = ledger.total_delegated.checked_sub(&amount).defensive_ok_or(ArithmeticError::Overflow)?; - ledger.unclaimed_withdrawals = - ledger.unclaimed_withdrawals.checked_sub(&amount).defensive_ok_or(ArithmeticError::Overflow)?; + ledger.total_delegated = ledger + .total_delegated + .checked_sub(&amount) + .defensive_ok_or(ArithmeticError::Overflow)?; + ledger.unclaimed_withdrawals = ledger + .unclaimed_withdrawals + .checked_sub(&amount) + .defensive_ok_or(ArithmeticError::Overflow)?; ledger.save(who); // book keep delegation - delegation.amount = delegation.amount.checked_sub(&amount).defensive_ok_or(ArithmeticError::Overflow)?; + delegation.amount = delegation + .amount + .checked_sub(&amount) + .defensive_ok_or(ArithmeticError::Overflow)?; // remove delegator if nothing delegated anymore if delegation.amount == BalanceOf::::zero() { diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index ce66ddbb3ba8..611c9cd3a8fd 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -459,7 +459,10 @@ mod integration { expected_proxy_delegated_amount ); // ED + stake amount is transferred from delegate to proxy delegator account. - assert_eq!(Balances::free_balance(200), 5000 - staked_amount - ExistentialDeposit::get()); + assert_eq!( + Balances::free_balance(200), + 5000 - staked_amount - ExistentialDeposit::get() + ); assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index fef5cfaccf5b..dfb0cefa542b 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,7 +373,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{EraIndex, StakingInterface, delegation::DelegationInterface}; +use sp_staking::{delegation::DelegationInterface, EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -2264,9 +2264,9 @@ pub mod pallet { ensure!(!withdrawn_points.is_empty(), Error::::CannotWithdrawAny); let mut sum_unlocked_points: BalanceOf = Zero::zero(); - let balance_to_unbond = withdrawn_points - .iter() - .fold(BalanceOf::::zero(), |accumulator, (era, unlocked_points)| { + let balance_to_unbond = withdrawn_points.iter().fold( + BalanceOf::::zero(), + |accumulator, (era, unlocked_points)| { sum_unlocked_points = sum_unlocked_points.saturating_add(*unlocked_points); if let Some(era_pool) = sub_pools.with_era.get_mut(era) { let balance_to_unbond = era_pool.dissolve(*unlocked_points); @@ -2279,15 +2279,20 @@ pub mod pallet { // era-less pool. accumulator.saturating_add(sub_pools.no_era.dissolve(*unlocked_points)) } - }); + }, + ); // fixme(ank4n): Transfer whatever is minimum transferable. // Withdraw upto limit and return the amount withdrawn and whether stash killed. // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the // `transferrable_balance` is correct. // fixme(ank4n): Handle result. - let _withdraw_result = - T::Staking::delegate_withdraw(&bonded_pool.bonded_account(), &member_account, balance_to_unbond, num_slashing_spans)?; + let _withdraw_result = T::Staking::delegate_withdraw( + &bonded_pool.bonded_account(), + &member_account, + balance_to_unbond, + num_slashing_spans, + )?; // defensive-only: the depositor puts enough funds into the stash so that it will only // be destroyed when they are leaving. @@ -2296,7 +2301,6 @@ pub mod pallet { // Error::::Defensive(DefensiveError::BondedStashKilledPrematurely) // ); - Self::deposit_event(Event::::Withdrawn { member: member_account.clone(), pool_id: member.pool_id, diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 15ded13ef745..4815607283a9 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -420,7 +420,6 @@ impl RewardDestination { }, } } - } /// Preference of what happens regarding validation. From 4e42c6c97512eac02ec5a79f44a4b2f3ac8ac943 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 17 Feb 2024 21:58:57 +0100 Subject: [PATCH 094/202] remove transactional --- substrate/frame/delegated-staking/src/impls.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index fb8a37db6ffe..0db1ee50e615 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -270,7 +270,6 @@ impl DelegationInterface for Pallet { } } - #[transactional] fn delegate_withdraw( delegate: &Self::AccountId, delegator: &Self::AccountId, From a4e0ee24f8b9c2f0f5ebed19de1641eab3dc079e Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 09:09:13 +0100 Subject: [PATCH 095/202] refactor but some test failing --- substrate/frame/delegated-staking/src/lib.rs | 93 ++++++++++++++++--- .../frame/delegated-staking/src/types.rs | 18 ++++ 2 files changed, 98 insertions(+), 13 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 98909b0e39fe..6154746a4318 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -44,17 +44,6 @@ //! to apply slash. It is `delegate`'s responsibility to apply slashes for each delegator, one at a //! time. Staking pallet ensures the pending slash never exceeds staked amount and would freeze //! further withdraws until pending slashes are applied. -//! -//! -//! ### Note about minimum balance -//! -//! When registering as `delegate`, a sub account for unclaimed withdrawals are created and funded -//! with `ExistentialDeposit`. If the `delegate` is migrating from being a `Nominator`, another -//! sub account is created for managing funds of delegators who have not yet migrated their funds -//! to won account. These accounts are funded by the `Delegate` and it should have enough free -//! balance to cover these. When these accounts are not killed (not needed anymore), the funds are -//! returned to the `Delegate`. - #[cfg(test)] mod mock; @@ -77,7 +66,9 @@ use frame_support::{ pallet_prelude::*, traits::{ fungible::{ - hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate}, + hold::{ + Balanced as FunHoldBalanced, Inspect as FunHoldInspect, Mutate as FunHoldMutate, + }, Balanced, Inspect as FunInspect, Mutate as FunMutate, }, tokens::{fungible::Credit, Fortitude, Precision, Preservation}, @@ -292,7 +283,25 @@ pub mod pallet { delegator: T::AccountId, amount: BalanceOf, ) -> DispatchResult { - todo!() + let delegate = ensure_signed(origin)?; + + // Ensure they have minimum delegation. + ensure!(amount >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); + + // Ensure delegator is sane. + ensure!(!Self::is_delegate(&delegator), Error::::NotAllowed); + ensure!(!Self::is_delegator(&delegator), Error::::NotAllowed); + ensure!(Self::not_direct_staker(&delegator), Error::::AlreadyStaker); + + // ensure delegate is sane. + ensure!(Self::is_delegate(&delegate), Error::::NotDelegate); + + // and has some delegated balance to migrate. + let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, delegate); + let balance_remaining = Self::held_balance_of(&proxy_delegator); + ensure!(balance_remaining >= amount, Error::::NotEnoughFunds); + + Self::do_migrate_delegation(&proxy_delegator, &delegator, amount) } /// Delegate funds to a `Delegate` account. @@ -328,6 +337,10 @@ impl Pallet { T::PalletId::get().into_sub_account_truncating((account_type, delegate_account.clone())) } + /// Balance of a delegator that is delegated. + pub(crate) fn held_balance_of(who: &T::AccountId) -> BalanceOf { + T::Currency::balance_on_hold(&HoldReason::Delegating.into(), who) + } fn is_delegate(who: &T::AccountId) -> bool { >::contains_key(who) } @@ -522,6 +535,60 @@ impl Pallet { Ok(ledger) } + + /// Migrates delegation of `amount` from `source` account to `destination` account. + fn do_migrate_delegation( + source_delegator: &T::AccountId, + destination_delegator: &T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { + let mut source_delegation = + Delegators::::get(&source_delegator).defensive_ok_or(Error::::BadState)?; + + // some checks that must have already been checked before. + ensure!(source_delegation.amount >= amount, Error::::NotEnoughFunds); + debug_assert!( + !Self::is_delegator(destination_delegator) && !Self::is_delegate(destination_delegator) + ); + + // update delegations + Delegation::::from(&source_delegation.delegate, amount) + .save(destination_delegator); + + source_delegation + .decrease_delegation(amount) + .defensive_ok_or(Error::::BadState)? + .save(source_delegator); + + // FIXME(ank4n): If all funds are migrated from source, it can be cleaned up and ED returned + // to delegate or alternatively whoever cleans it up. This could be a permission-less + // extrinsic. + + // release funds from source + let released = T::Currency::release( + &HoldReason::Delegating.into(), + &source_delegator, + amount, + Precision::BestEffort, + )?; + + defensive_assert!(released == amount, "hold should have been released fully"); + + // transfer the released value to `destination_delegator`. + // Note: The source should have been funded ED in the beginning so it should not be dusted. + T::Currency::transfer( + &source_delegator, + destination_delegator, + amount, + Preservation::Preserve, + ) + .map_err(|_| Error::::BadState)?; + + // hold the funds again in the new delegator account. + T::Currency::hold(&HoldReason::Delegating.into(), &destination_delegator, amount)?; + + Ok(()) + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 14121df2de70..a52cf7a760f9 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -49,7 +49,25 @@ impl Delegation { Delegation { delegate: delegate.clone(), amount } } + /// Checked decrease of delegation amount. Consumes self and returns a new copy. + pub(crate) fn decrease_delegation(self, amount: BalanceOf) -> Option { + let updated_delegation = self.amount.checked_sub(&amount)?; + Some(Delegation::from(&self.delegate, updated_delegation)) + } + + /// Checked increase of delegation amount. Consumes self and returns a new copy. + pub(crate) fn increase_delegation(self, amount: BalanceOf) -> Option { + let updated_delegation = self.amount.checked_add(&amount)?; + Some(Delegation::from(&self.delegate, updated_delegation)) + } + pub(crate) fn save(self, key: &T::AccountId) { + // Clean up if no delegation left. + if self.amount == Zero::zero() { + >::remove(key); + return; + } + >::insert(key, self) } } From 20c3fceddd566269aa013b658f9395d982378301 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 09:25:06 +0100 Subject: [PATCH 096/202] fix migration test --- .../frame/delegated-staking/src/impls.rs | 53 ++----------------- .../frame/delegated-staking/src/tests.rs | 2 +- 2 files changed, 5 insertions(+), 50 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 0db1ee50e615..be76e6a20ecf 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -318,61 +318,16 @@ impl DelegationInterface for Pallet { } /// Move funds from proxy delegator to actual delegator. - #[transactional] fn migrate_delegator( delegate: &Self::AccountId, new_delegator: &Self::AccountId, value: Self::Balance, ) -> DispatchResult { - ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - // make sure new delegator is not an existing delegator or a delegate - ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); - ensure!(!>::contains_key(new_delegator), Error::::NotAllowed); - // ensure we are migrating - let proxy_delegator = - >::get(delegate).ok_or(Error::::NotMigrating)?; - // proxy delegator must exist - // let (assigned_delegate, delegate_balance) = - let delegation = >::get(&proxy_delegator).ok_or(Error::::BadState)?; - ensure!(delegation.delegate == *delegate, Error::::BadState); - - // make sure proxy delegator has enough balance to support this migration. - ensure!(delegation.amount >= value, Error::::NotEnoughFunds); - - // remove delegation of `value` from `proxy_delegator`. - let updated_delegate_balance = delegation.amount.saturating_sub(value); - - // if all funds are migrated out of proxy delegator, clean up. - if updated_delegate_balance == BalanceOf::::zero() { - >::remove(&proxy_delegator); - >::remove(delegate); - } else { - // else update proxy delegator - >::insert( - &proxy_delegator, - Delegation { delegate: delegate.clone(), amount: updated_delegate_balance }, - ); - } - - let released = T::Currency::release( - &HoldReason::Delegating.into(), - &proxy_delegator, + Self::migrate_delegation( + RawOrigin::Signed(delegate.clone()).into(), + new_delegator.clone(), value, - Precision::BestEffort, - )?; - - defensive_assert!(released == value, "hold should have been released fully"); - - // transfer the withdrawn value to `new_delegator`. - T::Currency::transfer(&proxy_delegator, new_delegator, value, Preservation::Expendable) - .map_err(|_| Error::::BadState)?; - - // add the above removed delegation to `new_delegator`. - Delegation::::from(delegate, value).save(new_delegator); - // hold the funds again in the new delegator account. - T::Currency::hold(&HoldReason::Delegating.into(), &new_delegator, value)?; - - Ok(()) + ) } fn delegate( diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 611c9cd3a8fd..1a74f6137175 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -494,7 +494,7 @@ mod integration { // cannot use migrate delegator anymore assert_noop!( DelegatedStaking::migrate_delegator(&200, &305, 1), - Error::::NotMigrating + Error::::NotEnoughFunds ); }); } From bbc39b673bad44bdf074318d6b4ff8152b430399 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 10:50:03 +0100 Subject: [PATCH 097/202] impl delegation --- .../frame/delegated-staking/src/impls.rs | 42 +++---------------- substrate/frame/delegated-staking/src/lib.rs | 41 ++++++++++-------- .../frame/delegated-staking/src/tests.rs | 6 +-- .../frame/delegated-staking/src/types.rs | 18 +++++++- 4 files changed, 48 insertions(+), 59 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index be76e6a20ecf..50915e09f527 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -335,43 +335,11 @@ impl DelegationInterface for Pallet { delegate: &Self::AccountId, value: Self::Balance, ) -> DispatchResult { - let delegator_balance = - T::Currency::reducible_balance(&delegator, Preservation::Expendable, Fortitude::Polite); - ensure!(value >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - ensure!(delegator_balance >= value, Error::::NotEnoughFunds); - ensure!(delegate != delegator, Error::::InvalidDelegation); - - let mut delegation_register = - >::get(delegate).ok_or(Error::::NotDelegate)?; - ensure!(!delegation_register.blocked, Error::::DelegationsBlocked); - - // A delegate cannot delegate. - if >::contains_key(delegator) { - return Err(Error::::InvalidDelegation.into()) - } - - let new_delegation_amount = - if let Some(current_delegation) = >::get(delegator) { - ensure!(¤t_delegation.delegate == delegate, Error::::InvalidDelegation); - value.saturating_add(current_delegation.amount) - } else { - value - }; - - delegation_register.total_delegated.saturating_accrue(value); - - Delegation::::from(delegate, new_delegation_amount).save(delegator); - >::insert(delegate, delegation_register); - - T::Currency::hold(&HoldReason::Delegating.into(), &delegator, value)?; - - Self::deposit_event(Event::::Delegated { - delegate: delegate.clone(), - delegator: delegator.clone(), - amount: value, - }); - - Ok(()) + Self::delegate_funds( + RawOrigin::Signed(delegator.clone()).into(), + delegate.clone(), + value, + ) } } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 6154746a4318..78b4617e7033 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -153,8 +153,8 @@ pub mod pallet { NotSupported, /// This `delegate` is not set as a migrating account. NotMigrating, - /// Delegate no longer accepting new delegations. - DelegationsBlocked, + /// Account does not accept delegations. + NotAcceptingDelegations, } /// A reason for placing a hold on funds. @@ -305,26 +305,32 @@ pub mod pallet { } /// Delegate funds to a `Delegate` account. + /// + /// If delegation already exists, it increases the delegation by `amount`. #[pallet::call_index(4)] #[pallet::weight(Weight::default())] - // FIXME(ank4n): rename to `delegate` pub fn delegate_funds( origin: OriginFor, delegate: T::AccountId, amount: BalanceOf, ) -> DispatchResult { - todo!() - } + let who = ensure_signed(origin)?; - /// Add funds to an existing delegation. - #[pallet::call_index(5)] - #[pallet::weight(Weight::default())] - pub fn delegate_extra( - origin: OriginFor, - delegate: T::AccountId, - amount: BalanceOf, - ) -> DispatchResult { - todo!() + // ensure amount is over minimum to delegate + ensure!(amount > T::Currency::minimum_balance(), Error::::NotEnoughFunds); + + // ensure delegator is sane. + ensure!(Delegation::::can_delegate(&who, &delegate), Error::::InvalidDelegation); + ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); + + // ensure delegate is sane. + ensure!(DelegationLedger::::can_accept_delegation(&delegate), Error::::NotAcceptingDelegations); + + let delegator_balance = + T::Currency::reducible_balance(&who, Preservation::Preserve, Fortitude::Polite); + ensure!(delegator_balance >= amount, Error::::NotEnoughFunds); + + Self::do_delegate(&who, &delegate, amount) } } } @@ -413,7 +419,7 @@ impl Pallet { amount: BalanceOf, ) -> DispatchResult { let mut ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; - ensure!(!ledger.blocked, Error::::DelegationsBlocked); + debug_assert!(!ledger.blocked); let new_delegation_amount = if let Some(existing_delegation) = Delegation::::get(delegator) { @@ -552,8 +558,7 @@ impl Pallet { ); // update delegations - Delegation::::from(&source_delegation.delegate, amount) - .save(destination_delegator); + Delegation::::from(&source_delegation.delegate, amount).save(destination_delegator); source_delegation .decrease_delegation(amount) @@ -574,7 +579,7 @@ impl Pallet { defensive_assert!(released == amount, "hold should have been released fully"); - // transfer the released value to `destination_delegator`. + // transfer the released amount to `destination_delegator`. // Note: The source should have been funded ED in the beginning so it should not be dusted. T::Currency::transfer( &source_delegator, diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 1a74f6137175..7935b856b695 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -127,11 +127,11 @@ fn delegate_restrictions() { // delegate one tries to delegate to a delegator assert_noop!( DelegatedStaking::delegate(&delegate_one, &delegator_one, 10), - Error::::NotDelegate + Error::::InvalidDelegation ); assert_noop!( DelegatedStaking::delegate(&delegate_one, &delegator_two, 10), - Error::::NotDelegate + Error::::InvalidDelegation ); // delegator one tries to delegate to delegate 2 as well (it already delegates to delegate @@ -403,7 +403,7 @@ mod integration { // cannot delegate to it anymore assert_noop!( DelegatedStaking::delegate(&300, &200, 100), - Error::::DelegationsBlocked + Error::::NotAcceptingDelegations ); // delegate can unblock delegation diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index a52cf7a760f9..3f620386318a 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -49,6 +49,15 @@ impl Delegation { Delegation { delegate: delegate.clone(), amount } } + pub(crate) fn can_delegate(delegator: &T::AccountId, delegate: &T::AccountId) -> bool { + Delegation::::get(delegator) + .map(|delegation| delegation.delegate == delegate.clone()) + .unwrap_or( + // all good if its a new delegator expect it should not am existing delegate. + !>::contains_key(delegator) + ) + } + /// Checked decrease of delegation amount. Consumes self and returns a new copy. pub(crate) fn decrease_delegation(self, amount: BalanceOf) -> Option { let updated_delegation = self.amount.checked_sub(&amount)?; @@ -77,6 +86,7 @@ impl Delegation { /// This keeps track of the active balance of the `delegate` that is made up from the funds that are /// currently delegated to this `delegate`. It also tracks the pending slashes yet to be applied /// among other things. +// FIXME(ank4n): Break up into two storage items - bookkeeping stuff and settings stuff. #[derive(Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct DelegationLedger { @@ -115,6 +125,12 @@ impl DelegationLedger { >::get(key) } + pub(crate) fn can_accept_delegation(delegate: &T::AccountId) -> bool { + DelegationLedger::::get(delegate) + .map(|ledger| !ledger.blocked) + .unwrap_or(false) + } + /// Balance that is stakeable. pub(crate) fn delegated_balance(&self) -> BalanceOf { // do not allow to stake more than unapplied slash @@ -132,4 +148,4 @@ impl DelegationLedger { pub(crate) fn save(self, key: &T::AccountId) { >::insert(key, self) } -} +} \ No newline at end of file From 7634a58d75a40a1cc7a0c29d9838c718b08f728c Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 11:34:26 +0100 Subject: [PATCH 098/202] fix one test and leave a todo note for the other --- substrate/frame/delegated-staking/src/lib.rs | 12 +------ .../frame/delegated-staking/src/tests.rs | 34 +++++++++++-------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 78b4617e7033..007c889a2b4e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -151,8 +151,6 @@ pub mod pallet { WithdrawFailed, /// This operation is not supported with Delegation Staking. NotSupported, - /// This `delegate` is not set as a migrating account. - NotMigrating, /// Account does not accept delegations. NotAcceptingDelegations, } @@ -194,14 +192,6 @@ pub mod pallet { pub(crate) type Delegates = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; - /// Map of Delegate and its proxy delegator account while its actual delegators are migrating. - /// - /// Helps ensure correctness of ongoing migration of a direct nominator to a `delegate`. If a - /// `delegate` does not exist, it implies it is not going through migration. - #[pallet::storage] - pub(crate) type DelegateMigration = - CountedStorageMap<_, Twox64Concat, T::AccountId, T::AccountId, OptionQuery>; - #[pallet::call] impl Pallet { /// Register an account to be a `Delegate`. @@ -530,7 +520,7 @@ impl Pallet { let post_total = T::CoreStaking::stake(delegate).defensive()?.total; let new_withdrawn = - post_total.checked_sub(&pre_total).defensive_ok_or(Error::::BadState)?; + pre_total.checked_sub(&post_total).defensive_ok_or(Error::::BadState)?; ledger.unclaimed_withdrawals = ledger .unclaimed_withdrawals diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 7935b856b695..54cd92b53891 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -210,8 +210,8 @@ mod integration { assert!(eq_stake(delegate, total_staked, total_staked)); // Withdrawing without unbonding would fail. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &300, 50, 0), - Error::::WithdrawFailed + DelegatedStaking::delegate_withdraw(&delegate, &301, 50, 0), + Error::::NotEnoughFunds ); // assert_noop!(DelegatedStaking::delegate_withdraw(&delegate, &200, 50, 0), // Error::::NotAllowed); active and total stake remains same @@ -233,7 +233,7 @@ mod integration { // nothing to withdraw at era 4 assert_noop!( DelegatedStaking::delegate_withdraw(&delegate, &305, 50, 0), - Error::::WithdrawFailed + Error::::NotEnoughFunds ); assert!(eq_stake(delegate, total_staked, expected_active)); @@ -245,7 +245,7 @@ mod integration { // at era 5, 50 tokens are withdrawable, cannot withdraw more. assert_noop!( DelegatedStaking::delegate_withdraw(&delegate, &305, 51, 0), - Error::::WithdrawFailed + Error::::NotEnoughFunds ); // less is possible assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &305, 30, 0)); @@ -277,11 +277,11 @@ mod integration { // cannot withdraw anything more.. assert_noop!( DelegatedStaking::delegate_withdraw(&delegate, &301, 1, 0), - Error::::WithdrawFailed + Error::::NotEnoughFunds ); assert_noop!( DelegatedStaking::delegate_withdraw(&delegate, &350, 1, 0), - Error::::WithdrawFailed + Error::::NotEnoughFunds ); }); } @@ -295,19 +295,23 @@ mod integration { // verify withdraw not possible yet assert_noop!( DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0), - Error::::WithdrawFailed + Error::::NotEnoughFunds ); // add new delegation that is not staked - fund(&300, 1000); - assert_ok!(DelegatedStaking::delegate(&300, &delegate, 100)); - // verify unbonded balance - assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); - - // withdraw works now without unbonding - assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0)); - assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); + // FIXME(ank4n): add scenario where staked funds are withdrawn from ledger but not + // withdrawn and test its claimed from there first. + + // fund(&300, 1000); + // assert_ok!(DelegatedStaking::delegate(&300, &delegate, 100)); + // + // // verify unbonded balance + // assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); + // + // // withdraw works now without unbonding + // assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0)); + // assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); }); } From b05ff807c6d1605e31efaa348685311e66fb1ebc Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 11:53:15 +0100 Subject: [PATCH 099/202] moved some to use dispatch fn directly --- .../frame/delegated-staking/src/tests.rs | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 54cd92b53891..5bb65e3210f6 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -32,11 +32,11 @@ fn create_a_delegate_with_first_delegator() { // set intention to accept delegation. fund(&delegate, 1000); - assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_account)); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate).into(), reward_account)); // delegate to this account fund(&delegator, 1000); - assert_ok!(DelegatedStaking::delegate(&delegator, &delegate, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator).into(), delegate, 100)); // verify assert!(DelegatedStaking::is_delegate(&delegate)); @@ -50,23 +50,23 @@ fn cannot_become_delegate() { ExtBuilder::default().build_and_execute(|| { // cannot set reward account same as delegate account assert_noop!( - DelegatedStaking::accept_delegations(&100, &100), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(100).into(), 100), Error::::InvalidRewardDestination ); // an existing validator cannot become delegate assert_noop!( - DelegatedStaking::accept_delegations(&mock::GENESIS_VALIDATOR, &100), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(mock::GENESIS_VALIDATOR).into(), 100), Error::::AlreadyStaker ); // an existing nominator cannot become delegate assert_noop!( - DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_ONE, &100), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(mock::GENESIS_NOMINATOR_ONE).into(), 100), Error::::AlreadyStaker ); assert_noop!( - DelegatedStaking::accept_delegations(&mock::GENESIS_NOMINATOR_TWO, &100), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(mock::GENESIS_NOMINATOR_TWO).into(), 100), Error::::AlreadyStaker ); }); @@ -84,12 +84,12 @@ fn create_multiple_delegators() { assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); // set intention to accept delegation. - assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_account)); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate).into(), reward_account)); // create 100 delegators for i in 202..302 { fund(&i, 100 + ExistentialDeposit::get()); - assert_ok!(DelegatedStaking::delegate(&i, &delegate, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(i).into(), delegate, 100)); // Balance of 100 held on delegator account for delegating to the delegate. assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &i), 100); } @@ -107,37 +107,37 @@ fn delegate_restrictions() { let delegate_one = 200; let delegator_one = 210; fund(&delegate_one, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegate_one, &(delegate_one + 1))); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate_one).into(), delegate_one + 1)); fund(&delegator_one, 200); - assert_ok!(DelegatedStaking::delegate(&delegator_one, &delegate_one, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator_one).into(), delegate_one, 100)); let delegate_two = 300; let delegator_two = 310; fund(&delegate_two, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegate_two, &(delegate_two + 1))); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate_two).into(), delegate_two + 1)); fund(&delegator_two, 200); - assert_ok!(DelegatedStaking::delegate(&delegator_two, &delegate_two, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator_two).into(), delegate_two, 100)); // delegate one tries to delegate to delegate 2 assert_noop!( - DelegatedStaking::delegate(&delegate_one, &delegate_two, 10), + DelegatedStaking::delegate_funds(RawOrigin::Signed(delegate_one).into(), delegate_two, 10), Error::::InvalidDelegation ); // delegate one tries to delegate to a delegator assert_noop!( - DelegatedStaking::delegate(&delegate_one, &delegator_one, 10), + DelegatedStaking::delegate_funds(RawOrigin::Signed(delegate_one).into(), delegator_one, 10), Error::::InvalidDelegation ); assert_noop!( - DelegatedStaking::delegate(&delegate_one, &delegator_two, 10), + DelegatedStaking::delegate_funds(RawOrigin::Signed(delegate_one).into(), delegator_two, 10), Error::::InvalidDelegation ); // delegator one tries to delegate to delegate 2 as well (it already delegates to delegate // 1) assert_noop!( - DelegatedStaking::delegate(&delegator_one, &delegate_two, 10), + DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator_one).into(), delegate_two, 10), Error::::InvalidDelegation ); }); @@ -163,14 +163,14 @@ mod integration { // set intention to become a delegate fund(&delegate, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_acc)); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate).into(), reward_acc)); assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); let mut delegated_balance: Balance = 0; // set some delegations for delegator in 200..250 { fund(&delegator, 200); - assert_ok!(DelegatedStaking::delegate(&delegator, &delegate, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator).into(), delegate, 100)); delegated_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), @@ -210,10 +210,10 @@ mod integration { assert!(eq_stake(delegate, total_staked, total_staked)); // Withdrawing without unbonding would fail. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &301, 50, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 301, 50, 0), Error::::NotEnoughFunds ); - // assert_noop!(DelegatedStaking::delegate_withdraw(&delegate, &200, 50, 0), + // assert_noop!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 200, 50, 0), // Error::::NotAllowed); active and total stake remains same assert!(eq_stake(delegate, total_staked, total_staked)); @@ -232,7 +232,7 @@ mod integration { // nothing to withdraw at era 4 assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &305, 50, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 50, 0), Error::::NotEnoughFunds ); @@ -244,43 +244,43 @@ mod integration { start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &305, 51, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 51, 0), Error::::NotEnoughFunds ); // less is possible - assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &305, 30, 0)); - assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &305, 20, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 30, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 20, 0)); // Lets go to future era where everything is unbonded. Withdrawable amount: 100 + 200 start_era(7); // 305 has no more amount delegated so it cannot withdraw. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &305, 5, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 5, 0), Error::::NotDelegator ); // 309 is an active delegator but has total delegation of 90, so it cannot withdraw more // than that. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &309, 91, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 309, 91, 0), Error::::NotEnoughFunds ); // 310 cannot withdraw more than delegated funds. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &310, 101, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 310, 101, 0), Error::::NotEnoughFunds ); // but can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &310, 100, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 310, 100, 0)); // 320 can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &320, 200, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 320, 200, 0)); // cannot withdraw anything more.. assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &301, 1, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 301, 1, 0), Error::::NotEnoughFunds ); assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &350, 1, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 350, 1, 0), Error::::NotEnoughFunds ); }); @@ -294,7 +294,7 @@ mod integration { // verify withdraw not possible yet assert_noop!( - DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0), + DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, 0), Error::::NotEnoughFunds ); @@ -304,13 +304,13 @@ mod integration { // withdrawn and test its claimed from there first. // fund(&300, 1000); - // assert_ok!(DelegatedStaking::delegate(&300, &delegate, 100)); + // assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300.into()), delegate, 100)); // // // verify unbonded balance // assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); // // // withdraw works now without unbonding - // assert_ok!(DelegatedStaking::delegate_withdraw(&delegate, &300, 100, 0)); + // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, 0)); // assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); }); } @@ -324,15 +324,15 @@ mod integration { // `delegate` account cannot be reward destination assert_noop!( - DelegatedStaking::accept_delegations(&200, &200), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 200), Error::::InvalidRewardDestination ); // different reward account works - assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201)); // add some delegations to it fund(&300, 1000); - assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); // if delegate calls Staking pallet directly with a different reward destination, it // fails. @@ -377,16 +377,16 @@ mod integration { setup_delegation_stake(200, 201, (202..203).collect(), 100, 0); // Registering again is noop - assert_noop!(DelegatedStaking::accept_delegations(&200, &201), Error::::NotAllowed); + assert_noop!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201), Error::::NotAllowed); // a delegator cannot become delegate - assert_noop!(DelegatedStaking::accept_delegations(&202, &203), Error::::NotAllowed); + assert_noop!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(202).into(), 203), Error::::NotAllowed); // existing staker cannot become a delegate assert_noop!( - DelegatedStaking::accept_delegations(&GENESIS_NOMINATOR_ONE, &201), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(GENESIS_NOMINATOR_ONE).into(), 201), Error::::AlreadyStaker ); assert_noop!( - DelegatedStaking::accept_delegations(&GENESIS_VALIDATOR, &201), + DelegatedStaking::register_as_delegate(RawOrigin::Signed(GENESIS_VALIDATOR).into(), 201), Error::::AlreadyStaker ); }); @@ -395,18 +395,18 @@ mod integration { #[test] fn block_delegations() { ExtBuilder::default().build_and_execute(|| { - assert_ok!(DelegatedStaking::accept_delegations(&200, &201)); + assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201)); // delegation works fund(&300, 1000); - assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); // delegate blocks delegation assert_ok!(DelegatedStaking::block_delegations(&200)); // cannot delegate to it anymore assert_noop!( - DelegatedStaking::delegate(&300, &200, 100), + DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100), Error::::NotAcceptingDelegations ); @@ -414,7 +414,7 @@ mod integration { assert_ok!(DelegatedStaking::unblock_delegations(&200)); // delegation works again - assert_ok!(DelegatedStaking::delegate(&300, &200, 100)); + assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); }); } @@ -454,7 +454,7 @@ mod integration { // with at least ED. let proxy_delegator = DelegatedStaking::sub_account(AccountType::ProxyDelegator, 200); - assert_ok!(DelegatedStaking::migrate_accept_delegations(&200, &proxy_delegator, &201)); + assert_ok!(DelegatedStaking::migrate_to_delegate(RawOrigin::Signed(200).into(), 201)); // verify all went well let mut expected_proxy_delegated_amount = staked_amount; From cf18750ec8a4a78d7f1886ac839f3a4b8589db30 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 17:53:13 +0100 Subject: [PATCH 100/202] add Delegator type --- .../frame/delegated-staking/src/types.rs | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 3f620386318a..d316cbe2b784 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -19,6 +19,7 @@ //! Basic types used in delegated staking. use super::*; +use std::num::FpCategory::Zero; /// The type of pot account being created. #[derive(Encode, Decode)] @@ -54,7 +55,7 @@ impl Delegation { .map(|delegation| delegation.delegate == delegate.clone()) .unwrap_or( // all good if its a new delegator expect it should not am existing delegate. - !>::contains_key(delegator) + !>::contains_key(delegator), ) } @@ -95,10 +96,6 @@ pub struct DelegationLedger { /// Sum of all delegated funds to this `delegate`. #[codec(compact)] pub total_delegated: BalanceOf, - /// Amount that is bonded and held. - // FIXME(ank4n) (can we remove it) - #[codec(compact)] - pub hold: BalanceOf, /// Funds that are withdrawn from core staking but not released to delegator/s. #[codec(compact)] pub unclaimed_withdrawals: BalanceOf, @@ -114,7 +111,6 @@ impl DelegationLedger { DelegationLedger { payee: reward_destination.clone(), total_delegated: Zero::zero(), - hold: Zero::zero(), unclaimed_withdrawals: Zero::zero(), pending_slash: Zero::zero(), blocked: false, @@ -131,21 +127,47 @@ impl DelegationLedger { .unwrap_or(false) } - /// Balance that is stakeable. - pub(crate) fn delegated_balance(&self) -> BalanceOf { - // do not allow to stake more than unapplied slash + pub(crate) fn save(self, key: &T::AccountId) { + >::insert(key, self) + } + + /// Effective total balance of the `delegate`. + pub(crate) fn effective_balance(&self) -> BalanceOf { + defensive_assert!( + self.total_delegated > self.pending_slash, + "slash cannot be higher than actual balance of delegator" + ); + + // pending slash needs to be burned and cannot be used for stake. self.total_delegated.saturating_sub(self.pending_slash) } - /// Balance that is delegated but not bonded. - /// - /// Can be funds that are unbonded but not withdrawn. - pub(crate) fn unbonded_balance(&self) -> BalanceOf { - // fixme(ank4n) Remove hold and get balance from removing withdrawal_unclaimed. - self.total_delegated.saturating_sub(self.hold) + /// Balance that can be bonded in [`T::CoreStaking`]. + pub(crate) fn balance_available_to_bond(&self) -> BalanceOf { + self.effective_balance().saturating_sub(self.unclaimed_withdrawals) } +} - pub(crate) fn save(self, key: &T::AccountId) { - >::insert(key, self) +pub struct Delegate(T::AccountId); + +impl Delegate { + fn available_to_bond(&self) -> BalanceOf { + let exposed_stake = self.exposed_stake(); + + let bondable = self.ledger().map(|ledger| { + ledger.balance_available_to_bond() + }).unwrap_or(Zero::zero()); + + defensive_assert!(bondable >= exposed_stake, "cannot expose more than delegate balance"); + + bondable.saturating_sub(exposed_stake) } -} \ No newline at end of file + + fn ledger(&self) -> Option> { + DelegationLedger::::get(&self.0) + } + + fn exposed_stake(&self) -> BalanceOf { + T::CoreStaking::total_stake(&self.0).unwrap_or(Zero::zero()) + } +} From 2f9819af16ff4c58b671b5060f0142890c27b9c6 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 19:04:09 +0100 Subject: [PATCH 101/202] compiles --- .../frame/delegated-staking/src/impls.rs | 305 +++++++++--------- substrate/frame/delegated-staking/src/lib.rs | 32 +- substrate/frame/delegated-staking/src/mock.rs | 36 ++- .../frame/delegated-staking/src/tests.rs | 34 +- .../frame/delegated-staking/src/types.rs | 51 ++- 5 files changed, 255 insertions(+), 203 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 50915e09f527..258685226b2d 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -91,10 +91,10 @@ impl StakingInterface for Pallet { ) -> DispatchResult { // ensure who is not already staked ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegate); - let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; + let delegate = Delegate::::from(who)?; - ensure!(delegation_register.unbonded_balance() >= value, Error::::NotEnoughFunds); - ensure!(delegation_register.payee == *payee, Error::::InvalidRewardDestination); + ensure!(delegate.available_to_bond() >= value, Error::::NotEnoughFunds); + ensure!(delegate.ledger.payee == *payee, Error::::InvalidRewardDestination); T::CoreStaking::bond(who, value, payee) } @@ -111,19 +111,19 @@ impl StakingInterface for Pallet { fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; - ensure!(delegation_register.unbonded_balance() >= extra, Error::::NotEnoughFunds); + ensure!(delegation_register.stakeable_balance() >= extra, Error::::NotEnoughFunds); T::CoreStaking::bond_extra(who, extra) } fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { - let delegation_register = >::get(stash).ok_or(Error::::NotDelegate)?; - ensure!(delegation_register.hold >= value, Error::::NotEnoughFunds); + let delegate = Delegate::::from(stash)?; + ensure!(delegate.exposed_stake() >= value, Error::::NotEnoughFunds); T::CoreStaking::unbond(stash, value) } - /// Not supported, call [`Delegate::withdraw`] + /// Not supported, call [`DelegationInterface::delegate_withdraw`] fn withdraw_unbonded( _stash: Self::AccountId, _num_slashing_spans: u32, @@ -133,7 +133,7 @@ impl StakingInterface for Pallet { Err(Error::::NotSupported.into()) } - /// Not supported, call [`Delegate::withdraw`] + /// Not supported, call [`DelegationInterface::delegate_withdraw`] fn withdraw_exact( _stash: &Self::AccountId, _amount: Self::Balance, @@ -205,150 +205,150 @@ impl StakingInterface for Pallet { } } -impl DelegationInterface for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - fn delegated_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) - } - - fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who).map_or_else(|| 0u32.into(), |register| register.unbonded_balance()) - } - - fn accept_delegations( - who: &Self::AccountId, - reward_destination: &Self::AccountId, - ) -> DispatchResult { - Self::register_as_delegate( - RawOrigin::Signed(who.clone()).into(), - reward_destination.clone(), - ) - } - - /// Transfers funds from current staked account to `proxy_delegator`. Current staked account - /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. - fn migrate_accept_delegations( - who: &Self::AccountId, - _proxy_delegator: &Self::AccountId, - reward_destination: &Self::AccountId, - ) -> DispatchResult { - Self::migrate_to_delegate(RawOrigin::Signed(who.clone()).into(), reward_destination.clone()) - } - - fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { - let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; - register.blocked = true; - >::insert(delegate, register); - - Ok(()) - } - - fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult { - let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; - register.blocked = false; - >::insert(delegate, register); - - Ok(()) - } - - fn kill_delegate(_delegate: &Self::AccountId) -> DispatchResult { - todo!() - } - - fn bond_all(who: &Self::AccountId) -> DispatchResult { - let delegate = >::get(who).ok_or(Error::::NotDelegate)?; - let amount_to_bond = delegate.unbonded_balance(); - - match T::CoreStaking::stake(who) { - // already bonded - Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), - // first bond - Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegate.payee), - } - } - - fn delegate_withdraw( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - num_slashing_spans: u32, - ) -> DispatchResult { - Self::release( - RawOrigin::Signed(delegate.clone()).into(), - delegator.clone(), - value, - num_slashing_spans, - ) - } - - fn apply_slash( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, - ) -> DispatchResult { - let mut delegation_register = - >::get(delegate).ok_or(Error::::NotDelegate)?; - let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; - - ensure!(&delegation.delegate == delegate, Error::::NotDelegate); - ensure!(delegation.amount >= value, Error::::NotEnoughFunds); - - let (mut credit, _missing) = - T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); - let actual_slash = credit.peek(); - // remove the slashed amount - delegation_register.pending_slash.saturating_reduce(actual_slash); - >::insert(delegate, delegation_register); - - if let Some(reporter) = maybe_reporter { - let reward_payout: BalanceOf = - T::CoreStaking::slash_reward_fraction() * actual_slash; - let (reporter_reward, rest) = credit.split(reward_payout); - credit = rest; - // fixme(ank4n): handle error - let _ = T::Currency::resolve(&reporter, reporter_reward); - } - - T::OnSlash::on_unbalanced(credit); - Ok(()) - } - - /// Move funds from proxy delegator to actual delegator. - fn migrate_delegator( - delegate: &Self::AccountId, - new_delegator: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult { - Self::migrate_delegation( - RawOrigin::Signed(delegate.clone()).into(), - new_delegator.clone(), - value, - ) - } - - fn delegate( - delegator: &Self::AccountId, - delegate: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult { - Self::delegate_funds( - RawOrigin::Signed(delegator.clone()).into(), - delegate.clone(), - value, - ) - } -} +// impl DelegationInterface for Pallet { +// type Balance = BalanceOf; +// type AccountId = T::AccountId; +// +// fn delegated_balance(who: &Self::AccountId) -> Self::Balance { +// >::get(who) +// .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) +// } +// +// fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { +// >::get(who).map_or_else(|| 0u32.into(), |register| register.stakeable_balance()) +// } +// +// fn accept_delegations( +// who: &Self::AccountId, +// reward_destination: &Self::AccountId, +// ) -> DispatchResult { +// Self::register_as_delegate( +// RawOrigin::Signed(who.clone()).into(), +// reward_destination.clone(), +// ) +// } +// +// /// Transfers funds from current staked account to `proxy_delegator`. Current staked account +// /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. +// fn migrate_accept_delegations( +// who: &Self::AccountId, +// _proxy_delegator: &Self::AccountId, +// reward_destination: &Self::AccountId, +// ) -> DispatchResult { +// Self::migrate_to_delegate(RawOrigin::Signed(who.clone()).into(), reward_destination.clone()) +// } +// +// fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { +// let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; +// register.blocked = true; +// >::insert(delegate, register); +// +// Ok(()) +// } +// +// fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult { +// let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; +// register.blocked = false; +// >::insert(delegate, register); +// +// Ok(()) +// } +// +// fn kill_delegate(_delegate: &Self::AccountId) -> DispatchResult { +// todo!() +// } +// +// fn bond_all(who: &Self::AccountId) -> DispatchResult { +// let delegate = >::get(who).ok_or(Error::::NotDelegate)?; +// let amount_to_bond = delegate.stakeable_balance(); +// +// match T::CoreStaking::stake(who) { +// // already bonded +// Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), +// // first bond +// Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegate.payee), +// } +// } +// +// fn delegate_withdraw( +// delegate: &Self::AccountId, +// delegator: &Self::AccountId, +// value: Self::Balance, +// num_slashing_spans: u32, +// ) -> DispatchResult { +// Self::release( +// RawOrigin::Signed(delegate.clone()).into(), +// delegator.clone(), +// value, +// num_slashing_spans, +// ) +// } +// +// fn apply_slash( +// delegate: &Self::AccountId, +// delegator: &Self::AccountId, +// value: Self::Balance, +// maybe_reporter: Option, +// ) -> DispatchResult { +// let mut delegation_register = +// >::get(delegate).ok_or(Error::::NotDelegate)?; +// let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; +// +// ensure!(&delegation.delegate == delegate, Error::::NotDelegate); +// ensure!(delegation.amount >= value, Error::::NotEnoughFunds); +// +// let (mut credit, _missing) = +// T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); +// let actual_slash = credit.peek(); +// // remove the slashed amount +// delegation_register.pending_slash.saturating_reduce(actual_slash); +// >::insert(delegate, delegation_register); +// +// if let Some(reporter) = maybe_reporter { +// let reward_payout: BalanceOf = +// T::CoreStaking::slash_reward_fraction() * actual_slash; +// let (reporter_reward, rest) = credit.split(reward_payout); +// credit = rest; +// // fixme(ank4n): handle error +// let _ = T::Currency::resolve(&reporter, reporter_reward); +// } +// +// T::OnSlash::on_unbalanced(credit); +// Ok(()) +// } +// +// /// Move funds from proxy delegator to actual delegator. +// fn migrate_delegator( +// delegate: &Self::AccountId, +// new_delegator: &Self::AccountId, +// value: Self::Balance, +// ) -> DispatchResult { +// Self::migrate_delegation( +// RawOrigin::Signed(delegate.clone()).into(), +// new_delegator.clone(), +// value, +// ) +// } +// +// fn delegate( +// delegator: &Self::AccountId, +// delegate: &Self::AccountId, +// value: Self::Balance, +// ) -> DispatchResult { +// Self::delegate_funds( +// RawOrigin::Signed(delegator.clone()).into(), +// delegate.clone(), +// value, +// ) +// } +// } impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - >::get(who) - .map(|delegate| delegate.delegated_balance()) + Delegate::::from(who) + .map(|delegate| delegate.available_to_bond()) .unwrap_or_default() } @@ -380,16 +380,7 @@ impl StakingDelegationSupport for Pallet { } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); - - // delegation register should exist since `who` is a delegate. - let delegation_register = >::get(who).defensive_ok_or(Error::::BadState)?; - - ensure!(delegation_register.total_delegated >= amount, Error::::NotEnoughFunds); - ensure!(delegation_register.pending_slash <= amount, Error::::UnappliedSlash); - let updated_register = DelegationLedger { hold: amount, ..delegation_register }; - >::insert(who, updated_register); - + // fixme(ank4n): Do I really need this? Ok(()) } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 007c889a2b4e..5e9ba4b67be2 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -322,6 +322,23 @@ pub mod pallet { Self::do_delegate(&who, &delegate, amount) } + + /// Stop accepting new delegation. + /// + /// To unblock, pass false. + #[pallet::call_index(5)] + #[pallet::weight(Weight::default())] + pub fn block_delegations( + origin: OriginFor, + block: bool, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let delegate = Delegate::::from(&who)?; + delegate.update_status(block).save(); + + Ok(()) + } } } @@ -390,16 +407,15 @@ impl Pallet { Self::do_bond(who, stake.total) } - fn do_bond(delegate: &T::AccountId, amount: BalanceOf) -> DispatchResult { - let ledger = >::get(delegate).defensive_ok_or(Error::::NotDelegate)?; + fn do_bond(delegate_acc: &T::AccountId, amount: BalanceOf) -> DispatchResult { + let delegate = Delegate::::from(delegate_acc)?; - debug_assert!(amount == ledger.unbonded_balance()); + debug_assert!(amount == delegate.available_to_bond()); - match T::CoreStaking::stake(delegate) { - // already bonded - Ok(_) => T::CoreStaking::bond_extra(delegate, amount), - // first bond - Err(_) => T::CoreStaking::bond(delegate, amount, &ledger.payee), + if delegate.is_exposed() { + T::CoreStaking::bond_extra(&delegate.key, amount) + } else { + T::CoreStaking::bond(&delegate.key, amount, &delegate.reward_account()) } } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index f17c0cb2522d..978ff38dda05 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{self as delegated_staking}; +use crate::{self as delegated_staking, types::Delegate}; use frame_support::{ assert_ok, derive_impl, pallet_prelude::*, @@ -30,8 +30,9 @@ use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, }; +use frame_support::dispatch::RawOrigin; use pallet_staking::CurrentEra; -use sp_staking::delegation::{DelegationInterface, StakingDelegationSupport}; +use sp_staking::{delegation::StakingDelegationSupport, StakingInterface, Stake}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -250,20 +251,31 @@ pub(crate) fn setup_delegation_stake( increment: Balance, ) -> Balance { fund(&delegate, 100); - assert_ok!(DelegatedStaking::accept_delegations(&delegate, &reward_acc)); + assert_ok!(DelegatedStaking::register_as_delegate( + RawOrigin::Signed(delegate).into(), + reward_acc + )); let mut delegated_amount: Balance = 0; for (index, delegator) in delegators.iter().enumerate() { let amount_to_delegate = base_delegate_amount + increment * index as Balance; delegated_amount += amount_to_delegate; fund(delegator, amount_to_delegate + ExistentialDeposit::get()); - assert_ok!(DelegatedStaking::delegate(delegator, &delegate, amount_to_delegate)); - assert_ok!(DelegatedStaking::bond_all(&delegate)); + assert_ok!(DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegator.clone()).into(), + delegate, + amount_to_delegate + )); + if index == 0 { + assert_ok!(DelegatedStaking::bond(&delegate, amount_to_delegate, &reward_acc)); + } else { + assert_ok!(DelegatedStaking::bond_extra(&delegate, amount_to_delegate)); + } } // sanity checks assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_amount); - assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); + assert_eq!(Delegate::::from(&delegate).unwrap().available_to_bond(), 0); delegated_amount } @@ -273,6 +285,16 @@ pub(crate) fn start_era(era: sp_staking::EraIndex) { } pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool { - use sp_staking::{Stake, StakingInterface}; Staking::stake(&who).unwrap() == Stake { total, active } } + +pub(crate) fn delegate_available_to_bond(delegate: &AccountId) -> Balance { + let delegate = Delegate::::from(delegate).expect("delegate should exist"); + delegate.available_to_bond() +} + + +pub(crate) fn delegate_effective_balance(delegate: &AccountId) -> Balance { + let delegate = Delegate::::from(delegate).expect("delegate should exist"); + delegate.ledger.effective_balance() +} \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 5bb65e3210f6..aac6326bab19 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -180,10 +180,10 @@ mod integration { assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_balance); // unbonded balance is the newly delegated 100 - assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); - assert_ok!(DelegatedStaking::bond_all(&delegate)); + assert_eq!(delegate_available_to_bond(&delegate), 100); + assert_ok!(DelegatedStaking::bond_extra(&delegate, 100)); // after bond, unbonded balance is 0 - assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); + assert_eq!(delegate_available_to_bond(&delegate), 0); } assert_eq!( @@ -237,9 +237,9 @@ mod integration { ); assert!(eq_stake(delegate, total_staked, expected_active)); - assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); + assert_eq!(delegate_available_to_bond(&delegate), 0); // full amount is still delegated - assert_eq!(DelegatedStaking::delegated_balance(&delegate), total_staked); + assert_eq!(delegate_effective_balance(&delegate), total_staked); start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. @@ -307,11 +307,11 @@ mod integration { // assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300.into()), delegate, 100)); // // // verify unbonded balance - // assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 100); + // assert_eq!(delegate_available_to_bond(&delegate), 100); // // // withdraw works now without unbonding // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, 0)); - // assert_eq!(DelegatedStaking::unbonded_balance(&delegate), 0); + // assert_eq!(delegate_available_to_bond(&delegate), 0); }); } @@ -353,8 +353,8 @@ mod integration { )); // amount is staked correctly assert!(eq_stake(200, 100, 100)); - assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); - assert_eq!(DelegatedStaking::delegated_balance(&200), 100); + assert_eq!(delegate_available_to_bond(&200), 0); + assert_eq!(delegate_effective_balance(&200), 100); // free balance of delegate is untouched assert_eq!(Balances::free_balance(200), balance_200); @@ -402,7 +402,7 @@ mod integration { assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); // delegate blocks delegation - assert_ok!(DelegatedStaking::block_delegations(&200)); + assert_ok!(DelegatedStaking::block_delegations(RawOrigin::Signed(200).into(), true)); // cannot delegate to it anymore assert_noop!( @@ -411,7 +411,7 @@ mod integration { ); // delegate can unblock delegation - assert_ok!(DelegatedStaking::unblock_delegations(&200)); + assert_ok!(DelegatedStaking::block_delegations(RawOrigin::Signed(200).into(), false)); // delegation works again assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); @@ -468,8 +468,8 @@ mod integration { 5000 - staked_amount - ExistentialDeposit::get() ); assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); - assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); - assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); + assert_eq!(delegate_effective_balance(&200), 4000); + assert_eq!(delegate_available_to_bond(&200), 0); // now lets migrate the delegators let delegator_share = staked_amount / 4; @@ -478,7 +478,7 @@ mod integration { // fund them with ED fund(&delegator, ExistentialDeposit::get()); // migrate 1/4th amount into each delegator - assert_ok!(DelegatedStaking::migrate_delegator(&200, &delegator, delegator_share)); + assert_ok!(DelegatedStaking::migrate_delegation(RawOrigin::Signed(200).into(), delegator, delegator_share)); assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), delegator_share @@ -491,13 +491,13 @@ mod integration { // delegate stake is unchanged. assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); - assert_eq!(DelegatedStaking::delegated_balance(&200), 4000); - assert_eq!(DelegatedStaking::unbonded_balance(&200), 0); + assert_eq!(delegate_effective_balance(&200), 4000); + assert_eq!(delegate_available_to_bond(&200), 0); } // cannot use migrate delegator anymore assert_noop!( - DelegatedStaking::migrate_delegator(&200, &305, 1), + DelegatedStaking::migrate_delegation(RawOrigin::Signed(200).into(), 305, 1), Error::::NotEnoughFunds ); }); diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index d316cbe2b784..4248cecc9ab3 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -19,7 +19,6 @@ //! Basic types used in delegated staking. use super::*; -use std::num::FpCategory::Zero; /// The type of pot account being created. #[derive(Encode, Decode)] @@ -134,7 +133,7 @@ impl DelegationLedger { /// Effective total balance of the `delegate`. pub(crate) fn effective_balance(&self) -> BalanceOf { defensive_assert!( - self.total_delegated > self.pending_slash, + self.total_delegated >= self.pending_slash, "slash cannot be higher than actual balance of delegator" ); @@ -143,31 +142,55 @@ impl DelegationLedger { } /// Balance that can be bonded in [`T::CoreStaking`]. - pub(crate) fn balance_available_to_bond(&self) -> BalanceOf { + pub(crate) fn stakeable_balance(&self) -> BalanceOf { self.effective_balance().saturating_sub(self.unclaimed_withdrawals) } } -pub struct Delegate(T::AccountId); +pub struct Delegate { + pub key: T::AccountId, + pub ledger: DelegationLedger, +} impl Delegate { - fn available_to_bond(&self) -> BalanceOf { + pub(crate) fn from(delegate: &T::AccountId) -> Result, DispatchError> { + let ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; + Ok(Delegate { key: delegate.clone(), ledger }) + } + + pub(crate) fn available_to_bond(&self) -> BalanceOf { let exposed_stake = self.exposed_stake(); - let bondable = self.ledger().map(|ledger| { - ledger.balance_available_to_bond() - }).unwrap_or(Zero::zero()); + let stakeable = + self.ledger().map(|ledger| ledger.stakeable_balance()).unwrap_or(Zero::zero()); + + defensive_assert!(stakeable >= exposed_stake, "cannot expose more than delegate balance"); + + stakeable.saturating_sub(exposed_stake) + } - defensive_assert!(bondable >= exposed_stake, "cannot expose more than delegate balance"); + pub(crate) fn ledger(&self) -> Option> { + DelegationLedger::::get(&self.key) + } + + pub(crate) fn exposed_stake(&self) -> BalanceOf { + T::CoreStaking::total_stake(&self.key).unwrap_or(Zero::zero()) + } + + pub(crate) fn is_exposed(&self) -> bool { + T::CoreStaking::stake(&self.key).is_ok() + } - bondable.saturating_sub(exposed_stake) + pub(crate) fn reward_account(&self) -> &T::AccountId { + &self.ledger.payee } - fn ledger(&self) -> Option> { - DelegationLedger::::get(&self.0) + pub(crate) fn update_status(self, block: bool) -> Self { + Delegate { ledger: DelegationLedger { blocked: block, ..self.ledger }, ..self } } - fn exposed_stake(&self) -> BalanceOf { - T::CoreStaking::total_stake(&self.0).unwrap_or(Zero::zero()) + pub(crate) fn save(self) { + let key = self.key; + self.ledger.save(&key) } } From 50da2acc4e90900235d4283de55ec4251e357152 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 19:20:24 +0100 Subject: [PATCH 102/202] test passes --- .../frame/delegated-staking/src/impls.rs | 9 +- substrate/frame/delegated-staking/src/lib.rs | 18 +-- substrate/frame/delegated-staking/src/mock.rs | 10 +- .../frame/delegated-staking/src/tests.rs | 139 ++++++++++++++---- .../frame/delegated-staking/src/types.rs | 6 +- 5 files changed, 136 insertions(+), 46 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 258685226b2d..1a0e9ac5a592 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -118,7 +118,7 @@ impl StakingInterface for Pallet { fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { let delegate = Delegate::::from(stash)?; - ensure!(delegate.exposed_stake() >= value, Error::::NotEnoughFunds); + ensure!(delegate.bonded_stake() >= value, Error::::NotEnoughFunds); T::CoreStaking::unbond(stash, value) } @@ -346,9 +346,12 @@ impl StakingInterface for Pallet { impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; + + /// this balance is total delegator that can be staked, and importantly not extra balance that + /// is delegated but not bonded yet. fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { Delegate::::from(who) - .map(|delegate| delegate.available_to_bond()) + .map(|delegate| delegate.ledger.stakeable_balance()) .unwrap_or_default() } @@ -380,7 +383,7 @@ impl StakingDelegationSupport for Pallet { } fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - // fixme(ank4n): Do I really need this? + // fixme(ank4n): Do I really need this? Ok(()) } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 5e9ba4b67be2..c97369db6359 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -314,7 +314,10 @@ pub mod pallet { ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); // ensure delegate is sane. - ensure!(DelegationLedger::::can_accept_delegation(&delegate), Error::::NotAcceptingDelegations); + ensure!( + DelegationLedger::::can_accept_delegation(&delegate), + Error::::NotAcceptingDelegations + ); let delegator_balance = T::Currency::reducible_balance(&who, Preservation::Preserve, Fortitude::Polite); @@ -328,10 +331,7 @@ pub mod pallet { /// To unblock, pass false. #[pallet::call_index(5)] #[pallet::weight(Weight::default())] - pub fn block_delegations( - origin: OriginFor, - block: bool, - ) -> DispatchResult { + pub fn block_delegations(origin: OriginFor, block: bool) -> DispatchResult { let who = ensure_signed(origin)?; let delegate = Delegate::::from(&who)?; @@ -403,16 +403,16 @@ impl Pallet { // FIXME(ank4n) expose set payee in staking interface. // T::CoreStaking::set_payee(who, reward_account) - Self::do_delegate(&proxy_delegator, who, stake.total)?; - Self::do_bond(who, stake.total) + Self::do_delegate(&proxy_delegator, who, stake.total) } fn do_bond(delegate_acc: &T::AccountId, amount: BalanceOf) -> DispatchResult { let delegate = Delegate::::from(delegate_acc)?; - debug_assert!(amount == delegate.available_to_bond()); + let available_to_bond = delegate.available_to_bond(); + defensive_assert!(amount == available_to_bond, "not expected value to bond"); - if delegate.is_exposed() { + if delegate.is_bonded() { T::CoreStaking::bond_extra(&delegate.key, amount) } else { T::CoreStaking::bond(&delegate.key, amount, &delegate.reward_account()) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 978ff38dda05..c3a98e82354f 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -32,7 +32,7 @@ use frame_election_provider_support::{ }; use frame_support::dispatch::RawOrigin; use pallet_staking::CurrentEra; -use sp_staking::{delegation::StakingDelegationSupport, StakingInterface, Stake}; +use sp_staking::{delegation::StakingDelegationSupport, Stake, StakingInterface}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -293,8 +293,12 @@ pub(crate) fn delegate_available_to_bond(delegate: &AccountId) -> Balance { delegate.available_to_bond() } - pub(crate) fn delegate_effective_balance(delegate: &AccountId) -> Balance { let delegate = Delegate::::from(delegate).expect("delegate should exist"); delegate.ledger.effective_balance() -} \ No newline at end of file +} + +pub(crate) fn delegate_bonded(delegate: &AccountId) -> bool { + let delegate = Delegate::::from(delegate).expect("delegate should exist"); + delegate.is_bonded() +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index aac6326bab19..535ddf619a03 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -32,11 +32,18 @@ fn create_a_delegate_with_first_delegator() { // set intention to accept delegation. fund(&delegate, 1000); - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate).into(), reward_account)); + assert_ok!(DelegatedStaking::register_as_delegate( + RawOrigin::Signed(delegate).into(), + reward_account + )); // delegate to this account fund(&delegator, 1000); - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator).into(), delegate, 100)); + assert_ok!(DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegator).into(), + delegate, + 100 + )); // verify assert!(DelegatedStaking::is_delegate(&delegate)); @@ -56,17 +63,26 @@ fn cannot_become_delegate() { // an existing validator cannot become delegate assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(mock::GENESIS_VALIDATOR).into(), 100), + DelegatedStaking::register_as_delegate( + RawOrigin::Signed(mock::GENESIS_VALIDATOR).into(), + 100 + ), Error::::AlreadyStaker ); // an existing nominator cannot become delegate assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(mock::GENESIS_NOMINATOR_ONE).into(), 100), + DelegatedStaking::register_as_delegate( + RawOrigin::Signed(mock::GENESIS_NOMINATOR_ONE).into(), + 100 + ), Error::::AlreadyStaker ); assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(mock::GENESIS_NOMINATOR_TWO).into(), 100), + DelegatedStaking::register_as_delegate( + RawOrigin::Signed(mock::GENESIS_NOMINATOR_TWO).into(), + 100 + ), Error::::AlreadyStaker ); }); @@ -84,12 +100,19 @@ fn create_multiple_delegators() { assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); // set intention to accept delegation. - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate).into(), reward_account)); + assert_ok!(DelegatedStaking::register_as_delegate( + RawOrigin::Signed(delegate).into(), + reward_account + )); // create 100 delegators for i in 202..302 { fund(&i, 100 + ExistentialDeposit::get()); - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(i).into(), delegate, 100)); + assert_ok!(DelegatedStaking::delegate_funds( + RawOrigin::Signed(i).into(), + delegate, + 100 + )); // Balance of 100 held on delegator account for delegating to the delegate. assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &i), 100); } @@ -107,37 +130,67 @@ fn delegate_restrictions() { let delegate_one = 200; let delegator_one = 210; fund(&delegate_one, 100); - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate_one).into(), delegate_one + 1)); + assert_ok!(DelegatedStaking::register_as_delegate( + RawOrigin::Signed(delegate_one).into(), + delegate_one + 1 + )); fund(&delegator_one, 200); - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator_one).into(), delegate_one, 100)); + assert_ok!(DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegator_one).into(), + delegate_one, + 100 + )); let delegate_two = 300; let delegator_two = 310; fund(&delegate_two, 100); - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate_two).into(), delegate_two + 1)); + assert_ok!(DelegatedStaking::register_as_delegate( + RawOrigin::Signed(delegate_two).into(), + delegate_two + 1 + )); fund(&delegator_two, 200); - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator_two).into(), delegate_two, 100)); + assert_ok!(DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegator_two).into(), + delegate_two, + 100 + )); // delegate one tries to delegate to delegate 2 assert_noop!( - DelegatedStaking::delegate_funds(RawOrigin::Signed(delegate_one).into(), delegate_two, 10), + DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegate_one).into(), + delegate_two, + 10 + ), Error::::InvalidDelegation ); // delegate one tries to delegate to a delegator assert_noop!( - DelegatedStaking::delegate_funds(RawOrigin::Signed(delegate_one).into(), delegator_one, 10), + DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegate_one).into(), + delegator_one, + 10 + ), Error::::InvalidDelegation ); assert_noop!( - DelegatedStaking::delegate_funds(RawOrigin::Signed(delegate_one).into(), delegator_two, 10), + DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegate_one).into(), + delegator_two, + 10 + ), Error::::InvalidDelegation ); // delegator one tries to delegate to delegate 2 as well (it already delegates to delegate // 1) assert_noop!( - DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator_one).into(), delegate_two, 10), + DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegator_one).into(), + delegate_two, + 10 + ), Error::::InvalidDelegation ); }); @@ -163,14 +216,22 @@ mod integration { // set intention to become a delegate fund(&delegate, 100); - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(delegate).into(), reward_acc)); + assert_ok!(DelegatedStaking::register_as_delegate( + RawOrigin::Signed(delegate).into(), + reward_acc + )); assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); let mut delegated_balance: Balance = 0; + // set some delegations for delegator in 200..250 { fund(&delegator, 200); - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(delegator).into(), delegate, 100)); + assert_ok!(DelegatedStaking::delegate_funds( + RawOrigin::Signed(delegator).into(), + delegate, + 100 + )); delegated_balance += 100; assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), @@ -181,7 +242,12 @@ mod integration { // unbonded balance is the newly delegated 100 assert_eq!(delegate_available_to_bond(&delegate), 100); - assert_ok!(DelegatedStaking::bond_extra(&delegate, 100)); + if delegate_bonded(&delegate) { + assert_ok!(DelegatedStaking::bond_extra(&delegate, 100)); + } else { + assert_ok!(DelegatedStaking::bond(&delegate, 100, &reward_acc)); + } + // after bond, unbonded balance is 0 assert_eq!(delegate_available_to_bond(&delegate), 0); } @@ -213,8 +279,8 @@ mod integration { DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 301, 50, 0), Error::::NotEnoughFunds ); - // assert_noop!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 200, 50, 0), - // Error::::NotAllowed); active and total stake remains same + // assert_noop!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 200, 50, + // 0), Error::::NotAllowed); active and total stake remains same assert!(eq_stake(delegate, total_staked, total_staked)); // 305 wants to unbond 50 in era 2, withdrawable in era 5. @@ -304,14 +370,15 @@ mod integration { // withdrawn and test its claimed from there first. // fund(&300, 1000); - // assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300.into()), delegate, 100)); + // assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300.into()), delegate, + // 100)); // // // verify unbonded balance // assert_eq!(delegate_available_to_bond(&delegate), 100); // // // withdraw works now without unbonding - // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, 0)); - // assert_eq!(delegate_available_to_bond(&delegate), 0); + // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, + // 0)); assert_eq!(delegate_available_to_bond(&delegate), 0); }); } @@ -377,16 +444,28 @@ mod integration { setup_delegation_stake(200, 201, (202..203).collect(), 100, 0); // Registering again is noop - assert_noop!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201), Error::::NotAllowed); + assert_noop!( + DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201), + Error::::NotAllowed + ); // a delegator cannot become delegate - assert_noop!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(202).into(), 203), Error::::NotAllowed); + assert_noop!( + DelegatedStaking::register_as_delegate(RawOrigin::Signed(202).into(), 203), + Error::::NotAllowed + ); // existing staker cannot become a delegate assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(GENESIS_NOMINATOR_ONE).into(), 201), + DelegatedStaking::register_as_delegate( + RawOrigin::Signed(GENESIS_NOMINATOR_ONE).into(), + 201 + ), Error::::AlreadyStaker ); assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(GENESIS_VALIDATOR).into(), 201), + DelegatedStaking::register_as_delegate( + RawOrigin::Signed(GENESIS_VALIDATOR).into(), + 201 + ), Error::::AlreadyStaker ); }); @@ -478,7 +557,11 @@ mod integration { // fund them with ED fund(&delegator, ExistentialDeposit::get()); // migrate 1/4th amount into each delegator - assert_ok!(DelegatedStaking::migrate_delegation(RawOrigin::Signed(200).into(), delegator, delegator_share)); + assert_ok!(DelegatedStaking::migrate_delegation( + RawOrigin::Signed(200).into(), + delegator, + delegator_share + )); assert_eq!( Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), delegator_share diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 4248cecc9ab3..5999d4b9a977 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -159,7 +159,7 @@ impl Delegate { } pub(crate) fn available_to_bond(&self) -> BalanceOf { - let exposed_stake = self.exposed_stake(); + let exposed_stake = self.bonded_stake(); let stakeable = self.ledger().map(|ledger| ledger.stakeable_balance()).unwrap_or(Zero::zero()); @@ -173,11 +173,11 @@ impl Delegate { DelegationLedger::::get(&self.key) } - pub(crate) fn exposed_stake(&self) -> BalanceOf { + pub(crate) fn bonded_stake(&self) -> BalanceOf { T::CoreStaking::total_stake(&self.key).unwrap_or(Zero::zero()) } - pub(crate) fn is_exposed(&self) -> bool { + pub(crate) fn is_bonded(&self) -> bool { T::CoreStaking::stake(&self.key).is_ok() } From 2f00fb07cb7d436af9962e88f234ea24a36bbf0e Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 19:29:55 +0100 Subject: [PATCH 103/202] remove update hold --- substrate/frame/delegated-staking/src/impls.rs | 5 ----- substrate/frame/staking/src/pallet/impls.rs | 11 +++-------- substrate/primitives/staking/src/delegation.rs | 4 +--- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 1a0e9ac5a592..1e4a9df8294b 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -382,11 +382,6 @@ impl StakingDelegationSupport for Pallet { Self::is_delegate(who) } - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - // fixme(ank4n): Do I really need this? - Ok(()) - } - fn report_slash(who: &Self::AccountId, slash: Self::Balance) { >::mutate(who, |maybe_register| match maybe_register { Some(register) => register.pending_slash.saturating_accrue(slash), diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 3c6c24136a86..8d6e1c243b22 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1120,11 +1120,11 @@ impl Pallet { who: &T::AccountId, amount: BalanceOf, ) -> sp_runtime::DispatchResult { - if T::DelegationSupport::is_delegate(who) { - return T::DelegationSupport::update_hold(who, amount); + // only apply lock if it is not a delegate. Delegate accounts are already locked/held. + if !T::DelegationSupport::is_delegate(who) { + T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); } - T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); Ok(()) } } @@ -1885,11 +1885,6 @@ impl StakingDelegationSupport for NoDelegation { fn is_delegate(_who: &Self::AccountId) -> bool { false } - fn update_hold(_who: &Self::AccountId, _amount: Self::Balance) -> sp_runtime::DispatchResult { - defensive!("delegation update_hold should not be have been called for NoDelegation"); - Err(Error::::BadState.into()) - } - fn report_slash(_who: &Self::AccountId, _slash: Self::Balance) { defensive!("delegation report_slash should not be have been called for NoDelegation"); } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 6aa4befa339d..af8cca9bb091 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -21,6 +21,7 @@ use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; /// Allows an account to accept stake delegations and manage its operations. +// FIXME(ank4n): Remove this and add a new trait (in delegation pallet) for NP adapter. pub trait DelegationInterface { /// Balance type used by the staking system. type Balance: Sub @@ -154,9 +155,6 @@ pub trait StakingDelegationSupport { /// Returns true if `who` accepts delegations for stake. fn is_delegate(who: &Self::AccountId) -> bool; - /// Update amount held for bonded stake. - fn update_hold(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult; - /// Reports an ongoing slash to the `delegate` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } From 833eadb6a8fd071c07f3d3f1e0b0f66dbb81b6dd Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 20:04:51 +0100 Subject: [PATCH 104/202] remove commented code --- .../frame/delegated-staking/src/impls.rs | 103 +----------------- 1 file changed, 1 insertion(+), 102 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 1e4a9df8294b..c7ef966ce43e 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -206,84 +206,7 @@ impl StakingInterface for Pallet { } // impl DelegationInterface for Pallet { -// type Balance = BalanceOf; -// type AccountId = T::AccountId; -// -// fn delegated_balance(who: &Self::AccountId) -> Self::Balance { -// >::get(who) -// .map_or_else(|| 0u32.into(), |register| register.delegated_balance()) -// } -// -// fn unbonded_balance(who: &Self::AccountId) -> Self::Balance { -// >::get(who).map_or_else(|| 0u32.into(), |register| register.stakeable_balance()) -// } -// -// fn accept_delegations( -// who: &Self::AccountId, -// reward_destination: &Self::AccountId, -// ) -> DispatchResult { -// Self::register_as_delegate( -// RawOrigin::Signed(who.clone()).into(), -// reward_destination.clone(), -// ) -// } -// -// /// Transfers funds from current staked account to `proxy_delegator`. Current staked account -// /// becomes a `delegate` with `proxy_delegator` delegating stakes to it. -// fn migrate_accept_delegations( -// who: &Self::AccountId, -// _proxy_delegator: &Self::AccountId, -// reward_destination: &Self::AccountId, -// ) -> DispatchResult { -// Self::migrate_to_delegate(RawOrigin::Signed(who.clone()).into(), reward_destination.clone()) -// } -// -// fn block_delegations(delegate: &Self::AccountId) -> DispatchResult { -// let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; -// register.blocked = true; -// >::insert(delegate, register); -// -// Ok(()) -// } -// -// fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult { -// let mut register = >::get(delegate).ok_or(Error::::NotDelegate)?; -// register.blocked = false; -// >::insert(delegate, register); -// -// Ok(()) -// } -// -// fn kill_delegate(_delegate: &Self::AccountId) -> DispatchResult { -// todo!() -// } -// -// fn bond_all(who: &Self::AccountId) -> DispatchResult { -// let delegate = >::get(who).ok_or(Error::::NotDelegate)?; -// let amount_to_bond = delegate.stakeable_balance(); -// -// match T::CoreStaking::stake(who) { -// // already bonded -// Ok(_) => T::CoreStaking::bond_extra(who, amount_to_bond), -// // first bond -// Err(_) => T::CoreStaking::bond(who, amount_to_bond, &delegate.payee), -// } -// } -// -// fn delegate_withdraw( -// delegate: &Self::AccountId, -// delegator: &Self::AccountId, -// value: Self::Balance, -// num_slashing_spans: u32, -// ) -> DispatchResult { -// Self::release( -// RawOrigin::Signed(delegate.clone()).into(), -// delegator.clone(), -// value, -// num_slashing_spans, -// ) -// } -// +// FIXME(ank4n): Should be part of NP Adapter. // fn apply_slash( // delegate: &Self::AccountId, // delegator: &Self::AccountId, @@ -317,30 +240,6 @@ impl StakingInterface for Pallet { // Ok(()) // } // -// /// Move funds from proxy delegator to actual delegator. -// fn migrate_delegator( -// delegate: &Self::AccountId, -// new_delegator: &Self::AccountId, -// value: Self::Balance, -// ) -> DispatchResult { -// Self::migrate_delegation( -// RawOrigin::Signed(delegate.clone()).into(), -// new_delegator.clone(), -// value, -// ) -// } -// -// fn delegate( -// delegator: &Self::AccountId, -// delegate: &Self::AccountId, -// value: Self::Balance, -// ) -> DispatchResult { -// Self::delegate_funds( -// RawOrigin::Signed(delegator.clone()).into(), -// delegate.clone(), -// value, -// ) -// } // } impl StakingDelegationSupport for Pallet { From 9fe70fe9ee66fe1bf2a817d52de578b0b0c46ead Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 21:13:29 +0100 Subject: [PATCH 105/202] nomination pool revert --- .../nomination-pools/benchmarking/Cargo.toml | 3 - substrate/frame/nomination-pools/src/lib.rs | 70 ++++---- .../nomination-pools/test-staking/Cargo.toml | 1 - .../nomination-pools/test-staking/src/lib.rs | 163 ------------------ .../nomination-pools/test-staking/src/mock.rs | 18 +- 5 files changed, 39 insertions(+), 216 deletions(-) diff --git a/substrate/frame/nomination-pools/benchmarking/Cargo.toml b/substrate/frame/nomination-pools/benchmarking/Cargo.toml index c6f274340974..3693ad1866dd 100644 --- a/substrate/frame/nomination-pools/benchmarking/Cargo.toml +++ b/substrate/frame/nomination-pools/benchmarking/Cargo.toml @@ -27,7 +27,6 @@ frame-support = { path = "../../support", default-features = false } frame-system = { path = "../../system", default-features = false } pallet-bags-list = { path = "../../bags-list", default-features = false } pallet-staking = { path = "../../staking", default-features = false } -pallet-delegated-staking = { path = "../../delegated-staking", default-features = false } pallet-nomination-pools = { path = "..", default-features = false } # Substrate Primitives @@ -56,7 +55,6 @@ std = [ "pallet-balances/std", "pallet-nomination-pools/std", "pallet-staking/std", - "pallet-delegated-staking/std", "pallet-timestamp/std", "scale-info/std", "sp-core/std", @@ -76,7 +74,6 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-nomination-pools/runtime-benchmarks", "pallet-staking/runtime-benchmarks", - "pallet-delegated-staking/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index dfb0cefa542b..2777e7b6052e 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,7 +373,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{delegation::DelegationInterface, EraIndex, StakingInterface}; +use sp_staking::{EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -1259,7 +1259,7 @@ impl BondedPool { T::Currency::transfer( who, &bonded_account, - T::Currency::minimum_balance(), + amount, match ty { BondType::Create => Preservation::Expendable, BondType::Later => Preservation::Preserve, @@ -1271,19 +1271,11 @@ impl BondedPool { match ty { // TODO(ank4n): When type create, also do accept delegation call.. - BondType::Create => { - T::Staking::accept_delegations(&bonded_account, &self.reward_account())?; - T::Staking::delegate(&who, &bonded_account, amount)?; - T::Staking::bond(&bonded_account, amount, &self.reward_account())? - }, - + BondType::Create => T::Staking::bond(&bonded_account, amount, &self.reward_account())?, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. - BondType::Later => { - T::Staking::delegate(&who, &bonded_account, amount)?; - T::Staking::bond_extra(&bonded_account, amount)? - }, + BondType::Later => T::Staking::bond_extra(&bonded_account, amount)?, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); @@ -1652,8 +1644,7 @@ pub mod pallet { type U256ToBalance: Convert>; /// The interface for nominating. - type Staking: StakingInterface, AccountId = Self::AccountId> - + DelegationInterface, AccountId = Self::AccountId>; + type Staking: StakingInterface, AccountId = Self::AccountId>; /// The amount of eras a `SubPools::with_era` pool can exist before it gets merged into the /// `SubPools::no_era` pool. In other words, this is the amount of eras a member will be @@ -2263,10 +2254,22 @@ pub mod pallet { let withdrawn_points = member.withdraw_unlocked(current_era); ensure!(!withdrawn_points.is_empty(), Error::::CannotWithdrawAny); + // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the + // `transferrable_balance` is correct. + let stash_killed = + T::Staking::withdraw_unbonded(bonded_pool.bonded_account(), num_slashing_spans)?; + + // defensive-only: the depositor puts enough funds into the stash so that it will only + // be destroyed when they are leaving. + ensure!( + !stash_killed || caller == bonded_pool.roles.depositor, + Error::::Defensive(DefensiveError::BondedStashKilledPrematurely) + ); + let mut sum_unlocked_points: BalanceOf = Zero::zero(); - let balance_to_unbond = withdrawn_points.iter().fold( - BalanceOf::::zero(), - |accumulator, (era, unlocked_points)| { + let balance_to_unbond = withdrawn_points + .iter() + .fold(BalanceOf::::zero(), |accumulator, (era, unlocked_points)| { sum_unlocked_points = sum_unlocked_points.saturating_add(*unlocked_points); if let Some(era_pool) = sub_pools.with_era.get_mut(era) { let balance_to_unbond = era_pool.dissolve(*unlocked_points); @@ -2279,27 +2282,24 @@ pub mod pallet { // era-less pool. accumulator.saturating_add(sub_pools.no_era.dissolve(*unlocked_points)) } - }, - ); - // fixme(ank4n): Transfer whatever is minimum transferable. - // Withdraw upto limit and return the amount withdrawn and whether stash killed. - - // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the - // `transferrable_balance` is correct. - // fixme(ank4n): Handle result. - let _withdraw_result = T::Staking::delegate_withdraw( + }) + // A call to this transaction may cause the pool's stash to get dusted. If this + // happens before the last member has withdrawn, then all subsequent withdraws will + // be 0. However the unbond pools do no get updated to reflect this. In the + // aforementioned scenario, this check ensures we don't try to withdraw funds that + // don't exist. This check is also defensive in cases where the unbond pool does not + // update its balance (e.g. a bug in the slashing hook.) We gracefully proceed in + // order to ensure members can leave the pool and it can be destroyed. + .min(bonded_pool.transferable_balance()); + + // fixme(ank4n): Fix for delegated + T::Currency::transfer( &bonded_pool.bonded_account(), &member_account, balance_to_unbond, - num_slashing_spans, - )?; - - // defensive-only: the depositor puts enough funds into the stash so that it will only - // be destroyed when they are leaving. - // ensure!( - // !stash_killed || caller == bonded_pool.roles.depositor, - // Error::::Defensive(DefensiveError::BondedStashKilledPrematurely) - // ); + Preservation::Expendable, + ) + .defensive()?; Self::deposit_event(Event::::Withdrawn { member: member_account.clone(), diff --git a/substrate/frame/nomination-pools/test-staking/Cargo.toml b/substrate/frame/nomination-pools/test-staking/Cargo.toml index 12c358b7e161..9c7b12e4c634 100644 --- a/substrate/frame/nomination-pools/test-staking/Cargo.toml +++ b/substrate/frame/nomination-pools/test-staking/Cargo.toml @@ -32,7 +32,6 @@ frame-election-provider-support = { path = "../../election-provider-support" } pallet-timestamp = { path = "../../timestamp" } pallet-balances = { path = "../../balances" } pallet-staking = { path = "../../staking" } -pallet-delegated-staking = { path = "../../delegated-staking" } pallet-bags-list = { path = "../../bags-list" } pallet-staking-reward-curve = { path = "../../staking/reward-curve" } pallet-nomination-pools = { path = ".." } diff --git a/substrate/frame/nomination-pools/test-staking/src/lib.rs b/substrate/frame/nomination-pools/test-staking/src/lib.rs index e960d027f3e0..865b7a71e688 100644 --- a/substrate/frame/nomination-pools/test-staking/src/lib.rs +++ b/substrate/frame/nomination-pools/test-staking/src/lib.rs @@ -191,169 +191,6 @@ fn pool_lifecycle_e2e() { }) } -#[test] -fn pool_migration_to_delegation_e2e() { - new_test_ext().execute_with(|| { - assert_eq!(Balances::minimum_balance(), 5); - assert_eq!(Staking::current_era(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); - assert_eq!(LastPoolId::::get(), 1); - - // have the pool nominate. - assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, - ] - ); - - // have two members join - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true }, - ] - ); - - // pool goes into destroying - assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying)); - - // depositor cannot unbond yet. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - PoolsError::::MinimumBondNotMet, - ); - - // now the members want to unbond. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); - - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_eras.len(), 1); - assert_eq!(PoolMembers::::get(20).unwrap().points, 0); - assert_eq!(PoolMembers::::get(21).unwrap().unbonding_eras.len(), 1); - assert_eq!(PoolMembers::::get(21).unwrap().points, 0); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, - PoolsEvent::Unbonded { member: 20, pool_id: 1, points: 10, balance: 10, era: 3 }, - PoolsEvent::Unbonded { member: 21, pool_id: 1, points: 10, balance: 10, era: 3 }, - ] - ); - - // depositor cannot still unbond - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - PoolsError::::MinimumBondNotMet, - ); - - for e in 1..BondingDuration::get() { - CurrentEra::::set(Some(e)); - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0), - PoolsError::::CannotWithdrawAny - ); - } - - // members are now unlocked. - CurrentEra::::set(Some(BondingDuration::get())); - - // depositor cannot still unbond - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - PoolsError::::MinimumBondNotMet, - ); - - // but members can now withdraw. - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); - assert!(PoolMembers::::get(20).is_none()); - assert!(PoolMembers::::get(21).is_none()); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, - PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }, - ] - ); - - // as soon as all members have left, the depositor can try to unbond, but since the - // min-nominator intention is set, they must chill first. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - pallet_staking::Error::::InsufficientBond - ); - - assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Chilled { stash: POOL1_BONDED }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 50, balance: 50, era: 6 }] - ); - - // waiting another bonding duration: - CurrentEra::::set(Some(BondingDuration::get() * 2)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1)); - - // pools is fully destroyed now. - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, - PoolsEvent::Destroyed { pool_id: 1 } - ] - ); - }) -} - #[test] fn pool_slash_e2e() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 751baedc7557..ce97e13d640b 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -88,8 +88,8 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); } pallet_staking_reward_curve::build! { @@ -111,7 +111,6 @@ parameter_types! { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; - type DelegationSupport = DelegatedStaking; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); @@ -168,14 +167,6 @@ impl Convert for U256ToBalance { } } -impl pallet_delegated_staking::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type OnSlash = (); - type RuntimeHoldReason = RuntimeHoldReason; - type CoreStaking = Staking; -} - parameter_types! { pub const PostUnbondingPoolsWindow: u32 = 10; pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -189,7 +180,7 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type Staking = DelegatedStaking; + type Staking = Staking; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = ConstU32<8>; @@ -200,14 +191,13 @@ impl pallet_nomination_pools::Config for Runtime { type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( -pub enum Runtime { + pub enum Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, Staking: pallet_staking, VoterList: pallet_bags_list::, Pools: pallet_nomination_pools, - DelegatedStaking: pallet_delegated_staking::{Pallet, Storage, Event, HoldReason}, } ); From bdc3b12d7f8903d48d71121c693924ace4277ec5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 21:19:48 +0100 Subject: [PATCH 106/202] fix np tests --- Cargo.lock | 2 -- substrate/frame/delegated-staking/src/impls.rs | 10 ---------- substrate/frame/nomination-pools/src/mock.rs | 8 ++++++++ .../frame/nomination-pools/test-staking/src/mock.rs | 1 + substrate/frame/staking/src/pallet/impls.rs | 10 ---------- substrate/primitives/staking/src/lib.rs | 9 --------- 6 files changed, 9 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 778c0ba3b11a..09a70664d1fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10372,7 +10372,6 @@ dependencies = [ "frame-system", "pallet-bags-list", "pallet-balances", - "pallet-delegated-staking", "pallet-nomination-pools", "pallet-staking", "pallet-staking-reward-curve", @@ -10422,7 +10421,6 @@ dependencies = [ "log", "pallet-bags-list", "pallet-balances", - "pallet-delegated-staking", "pallet-nomination-pools", "pallet-staking", "pallet-staking-reward-curve", diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index c7ef966ce43e..f8cc15f597d7 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -133,16 +133,6 @@ impl StakingInterface for Pallet { Err(Error::::NotSupported.into()) } - /// Not supported, call [`DelegationInterface::delegate_withdraw`] - fn withdraw_exact( - _stash: &Self::AccountId, - _amount: Self::Balance, - _num_slashing_spans: u32, - ) -> Result { - defensive_assert!(false, "not supported for delegated impl of staking interface"); - Err(Error::::NotSupported.into()) - } - fn desired_validator_count() -> u32 { defensive_assert!(false, "not supported for delegated impl of staking interface"); T::CoreStaking::desired_validator_count() diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index f982b72c6356..8110b4e7fd06 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -220,6 +220,14 @@ impl sp_staking::StakingInterface for StakingMock { fn max_exposure_page_size() -> sp_staking::Page { unimplemented!("method currently not used in testing") } + + fn slash_reward_fraction() -> Perbill { + unimplemented!("method currently not used in testing") + } + + fn release_all(_who: &Self::AccountId) { + unimplemented!("method currently not used in testing") + } } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index ce97e13d640b..87ce77f9733a 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -111,6 +111,7 @@ parameter_types! { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; + type DelegationSupport = pallet_staking::NoDelegation; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 242ccd0bd605..08ab7c81afb1 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1769,16 +1769,6 @@ impl StakingInterface for Pallet { .map_err(|with_post| with_post.error) } - fn withdraw_exact( - who: &Self::AccountId, - amount: BalanceOf, - num_slashing_spans: u32, - ) -> Result { - let ctrl = Self::bonded(who).ok_or(Error::::NotStash)?; - Self::do_withdraw_unbonded(&ctrl, num_slashing_spans, Some(amount)) - .map(|_| !Ledger::::contains_key(&ctrl)) - } - fn bond( who: &Self::AccountId, value: Self::Balance, diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 4819ea583a27..46825775fd62 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -251,15 +251,6 @@ pub trait StakingInterface { num_slashing_spans: u32, ) -> Result; - /// Unlock any funds schedule to unlock before or at the current era upto a provided limit. - /// - /// Returns whether the stash was killed because of this withdraw or not. - fn withdraw_exact( - stash: &Self::AccountId, - amount: Self::Balance, - num_slashing_spans: u32, - ) -> Result; - /// The ideal number of active validators. fn desired_validator_count() -> u32; From 949c5d512728be1bbba940714777e1a9cf016e13 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 21:25:38 +0100 Subject: [PATCH 107/202] refactor test --- substrate/frame/delegated-staking/src/mock.rs | 14 +++------- .../frame/delegated-staking/src/tests.rs | 26 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index c3a98e82354f..e001444b5fce 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -288,17 +288,11 @@ pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool Staking::stake(&who).unwrap() == Stake { total, active } } -pub(crate) fn delegate_available_to_bond(delegate: &AccountId) -> Balance { - let delegate = Delegate::::from(delegate).expect("delegate should exist"); - delegate.available_to_bond() -} - -pub(crate) fn delegate_effective_balance(delegate: &AccountId) -> Balance { - let delegate = Delegate::::from(delegate).expect("delegate should exist"); - delegate.ledger.effective_balance() -} - pub(crate) fn delegate_bonded(delegate: &AccountId) -> bool { let delegate = Delegate::::from(delegate).expect("delegate should exist"); delegate.is_bonded() } + +pub(crate) fn get_delegate(delegate: &AccountId) -> Delegate { + Delegate::::from(delegate).expect("delegate should exist") +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 535ddf619a03..69264bfec036 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -241,15 +241,15 @@ mod integration { assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_balance); // unbonded balance is the newly delegated 100 - assert_eq!(delegate_available_to_bond(&delegate), 100); - if delegate_bonded(&delegate) { + assert_eq!(get_delegate(&delegate).available_to_bond(), 100); + if get_delegate(&delegate).is_bonded() { assert_ok!(DelegatedStaking::bond_extra(&delegate, 100)); } else { assert_ok!(DelegatedStaking::bond(&delegate, 100, &reward_acc)); } // after bond, unbonded balance is 0 - assert_eq!(delegate_available_to_bond(&delegate), 0); + assert_eq!(get_delegate(&delegate).available_to_bond(), 0); } assert_eq!( @@ -303,9 +303,9 @@ mod integration { ); assert!(eq_stake(delegate, total_staked, expected_active)); - assert_eq!(delegate_available_to_bond(&delegate), 0); + assert_eq!(get_delegate(&delegate).available_to_bond(), 0); // full amount is still delegated - assert_eq!(delegate_effective_balance(&delegate), total_staked); + assert_eq!(get_delegate(&delegate).ledger.effective_balance(), total_staked); start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. @@ -374,11 +374,11 @@ mod integration { // 100)); // // // verify unbonded balance - // assert_eq!(delegate_available_to_bond(&delegate), 100); + // assert_eq!(get_delegate(&delegate).available_to_bond(), 100); // // // withdraw works now without unbonding // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, - // 0)); assert_eq!(delegate_available_to_bond(&delegate), 0); + // 0)); assert_eq!(get_delegate(&delegate).available_to_bond(), 0); }); } @@ -420,8 +420,8 @@ mod integration { )); // amount is staked correctly assert!(eq_stake(200, 100, 100)); - assert_eq!(delegate_available_to_bond(&200), 0); - assert_eq!(delegate_effective_balance(&200), 100); + assert_eq!(get_delegate(&200).available_to_bond(), 0); + assert_eq!(get_delegate(&200).ledger.effective_balance(), 100); // free balance of delegate is untouched assert_eq!(Balances::free_balance(200), balance_200); @@ -547,8 +547,8 @@ mod integration { 5000 - staked_amount - ExistentialDeposit::get() ); assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); - assert_eq!(delegate_effective_balance(&200), 4000); - assert_eq!(delegate_available_to_bond(&200), 0); + assert_eq!(get_delegate(&200).ledger.effective_balance(), 4000); + assert_eq!(get_delegate(&200).available_to_bond(), 0); // now lets migrate the delegators let delegator_share = staked_amount / 4; @@ -574,8 +574,8 @@ mod integration { // delegate stake is unchanged. assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); - assert_eq!(delegate_effective_balance(&200), 4000); - assert_eq!(delegate_available_to_bond(&200), 0); + assert_eq!(get_delegate(&200).ledger.effective_balance(), 4000); + assert_eq!(get_delegate(&200).available_to_bond(), 0); } // cannot use migrate delegator anymore From f31cee29bc5a0cddde3f14c162137a9b468d9247 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 18 Feb 2024 22:25:43 +0100 Subject: [PATCH 108/202] create np direct stake adapter --- .../frame/delegated-staking/src/impls.rs | 4 +- substrate/frame/delegated-staking/src/lib.rs | 1 + .../frame/nomination-pools/src/adapter.rs | 81 +++++++++++++++++++ substrate/frame/nomination-pools/src/lib.rs | 20 +++-- substrate/frame/nomination-pools/src/mock.rs | 1 + 5 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 substrate/frame/nomination-pools/src/adapter.rs diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index f8cc15f597d7..5db6bbb333ee 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -16,8 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Implementations of public traits, namely [StakingInterface], [DelegationInterface] and -//! [StakingDelegationSupport]. +//! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. use super::*; @@ -124,6 +123,7 @@ impl StakingInterface for Pallet { } /// Not supported, call [`DelegationInterface::delegate_withdraw`] + /// FIXME(ank4n): Support it!! fn withdraw_unbonded( _stash: Self::AccountId, _num_slashing_spans: u32, diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index c97369db6359..5527187f20ad 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] +// FIXME(ank4n): fix docs //! An implementation of a delegation system for staking that can be utilised using //! [`DelegationInterface`]. In future, if exposed via extrinsic, these primitives could also be //! used by off-chain entities, or by foreign multi-locations (via xcm). diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs new file mode 100644 index 000000000000..1fa02379496b --- /dev/null +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -0,0 +1,81 @@ +// This file is part of Substrate. + +// Copyright (C) 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 crate::*; +use frame_support::traits::tokens::Balance; + +/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. +pub trait PoolAdapter { + type Balance: Balance; + type AccountId: Clone + Debug; + + /// Similar to [Inspect::balance]. + fn balance(who: &Self::AccountId) -> Self::Balance; + + /// Similar to [Inspect::total_balance]. + fn total_balance(who: &Self::AccountId) -> Self::Balance; + + /// Delegates stake from delegator to pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, preservation: Preservation) -> DispatchResult; + + /// Revoke delegation to pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. + fn release_delegation(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + +} + + +/// Pool adapter that supports DirectStake. +pub struct DirectStake(PhantomData); + +impl PoolAdapter for DirectStake { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::balance(who) + } + + fn total_balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::total_balance(who) + } + + fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, preservation: Preservation) -> DispatchResult { + T::Currency::transfer( + who, + &pool_account, + amount, + preservation, + )?; + + Ok(()) + } + + fn release_delegation(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + T::Currency::transfer( + &pool_account, + &who, + amount, + Preservation::Expendable, + )?; + + Ok(()) + } +} diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 2777e7b6052e..b8848dea969d 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -378,6 +378,7 @@ use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] use sp_runtime::TryRuntimeError; +use adapter::PoolAdapter; /// The log target of this pallet. pub const LOG_TARGET: &str = "runtime::nomination-pools"; @@ -399,6 +400,7 @@ mod tests; pub mod migration; pub mod weights; +mod adapter; pub use pallet::*; pub use weights::WeightInfo; @@ -1056,7 +1058,7 @@ impl BondedPool { // `pallet-nomination-pool`. This means reducible balance always returns balance preserving // ED in the account. What we want though is transferable balance given the account can be // dusted. - T::Currency::balance(&account) + T::PoolAdapter::balance(&account) .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) } @@ -1256,7 +1258,7 @@ impl BondedPool { // Cache the value let bonded_account = self.bonded_account(); // TODO(ank4n) joining a pool: delegate funds to pool account.. - T::Currency::transfer( + T::PoolAdapter::delegate( who, &bonded_account, amount, @@ -1655,6 +1657,9 @@ pub mod pallet { /// The maximum length, in bytes, that a pools metadata maybe. type MaxMetadataLen: Get; + + /// An adapter to support delegated to direct staking. + type PoolAdapter: PoolAdapter>; } /// The sum of funds across all pools. @@ -2292,12 +2297,10 @@ pub mod pallet { // order to ensure members can leave the pool and it can be destroyed. .min(bonded_pool.transferable_balance()); - // fixme(ank4n): Fix for delegated - T::Currency::transfer( - &bonded_pool.bonded_account(), + T::PoolAdapter::release_delegation( &member_account, + &bonded_pool.bonded_account(), balance_to_unbond, - Preservation::Expendable, ) .defensive()?; @@ -2879,11 +2882,12 @@ impl Pallet { "could not transfer all amount to depositor while dissolving pool" ); defensive_assert!( - T::Currency::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), + T::PoolAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), "dissolving pool should not have any balance" ); // NOTE: Defensively force set balance to zero. T::Currency::set_balance(&reward_account, Zero::zero()); + // fixme(ank4n): Can't do this with delegated? T::Currency::set_balance(&bonded_pool.bonded_account(), Zero::zero()); Self::deposit_event(Event::::Destroyed { pool_id: bonded_pool.id }); @@ -3460,7 +3464,7 @@ impl Pallet { let sum_unbonding_balance = subs.sum_unbonding_balance(); let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default(); - let total_balance = T::Currency::total_balance(&pool_account); + let total_balance = T::PoolAdapter::total_balance(&pool_account); assert!( total_balance >= bonded_balance + sum_unbonding_balance, diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 8110b4e7fd06..b60f4a26b3ac 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -311,6 +311,7 @@ impl pools::Config for Runtime { type MaxMetadataLen = MaxMetadataLen; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; + type PoolAdapter = adapter::DirectStake; } type Block = frame_system::mocking::MockBlock; From 141004f302c890893913124ce0e9be09c9a786ef Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 19 Feb 2024 09:29:51 +0100 Subject: [PATCH 109/202] all test pass --- .../frame/nomination-pools/src/adapter.rs | 41 ++++++--------- substrate/frame/nomination-pools/src/lib.rs | 26 ++++------ .../nomination-pools/test-staking/src/mock.rs | 1 + .../primitives/staking/src/delegation.rs | 50 +++++++++++++++++++ 4 files changed, 75 insertions(+), 43 deletions(-) diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 1fa02379496b..335a23850067 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -18,34 +18,10 @@ use crate::*; use frame_support::traits::tokens::Balance; -/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. -pub trait PoolAdapter { - type Balance: Balance; - type AccountId: Clone + Debug; - - /// Similar to [Inspect::balance]. - fn balance(who: &Self::AccountId) -> Self::Balance; - - /// Similar to [Inspect::total_balance]. - fn total_balance(who: &Self::AccountId) -> Self::Balance; - - /// Delegates stake from delegator to pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. - fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, preservation: Preservation) -> DispatchResult; - - /// Revoke delegation to pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. - fn release_delegation(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult; - -} - - /// Pool adapter that supports DirectStake. pub struct DirectStake(PhantomData); -impl PoolAdapter for DirectStake { +impl sp_staking::delegation::PoolAdapter for DirectStake { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -57,12 +33,23 @@ impl PoolAdapter for DirectStake { T::Currency::total_balance(who) } - fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, preservation: Preservation) -> DispatchResult { + fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + T::Currency::transfer( + who, + &pool_account, + amount, + Preservation::Expendable, + )?; + + Ok(()) + } + + fn delegate_extra(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { T::Currency::transfer( who, &pool_account, amount, - preservation, + Preservation::Preserve, )?; Ok(()) diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index b8848dea969d..4566494dfb56 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,12 +373,11 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{EraIndex, StakingInterface}; +use sp_staking::{EraIndex, StakingInterface, delegation::PoolAdapter}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] use sp_runtime::TryRuntimeError; -use adapter::PoolAdapter; /// The log target of this pallet. pub const LOG_TARGET: &str = "runtime::nomination-pools"; @@ -400,7 +399,7 @@ mod tests; pub mod migration; pub mod weights; -mod adapter; +pub mod adapter; pub use pallet::*; pub use weights::WeightInfo; @@ -1257,27 +1256,22 @@ impl BondedPool { ) -> Result, DispatchError> { // Cache the value let bonded_account = self.bonded_account(); - // TODO(ank4n) joining a pool: delegate funds to pool account.. - T::PoolAdapter::delegate( - who, - &bonded_account, - amount, - match ty { - BondType::Create => Preservation::Expendable, - BondType::Later => Preservation::Preserve, - }, - )?; + // We must calculate the points issued *before* we bond who's funds, else points:balance // ratio will be wrong. let points_issued = self.issue(amount); match ty { - // TODO(ank4n): When type create, also do accept delegation call.. - BondType::Create => T::Staking::bond(&bonded_account, amount, &self.reward_account())?, + BondType::Create => { + T::PoolAdapter::delegate(who, &bonded_account, amount)?; + T::Staking::bond(&bonded_account, amount, &self.reward_account())? + }, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. - BondType::Later => T::Staking::bond_extra(&bonded_account, amount)?, + BondType::Later => { + T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?; + T::Staking::bond_extra(&bonded_account, amount)? }, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 87ce77f9733a..6b327ba5bb27 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -187,6 +187,7 @@ impl pallet_nomination_pools::Config for Runtime { type MaxUnbonding = ConstU32<8>; type MaxPointsToBalance = ConstU8<10>; type PalletId = PoolsPalletId; + type PoolAdapter = pallet_nomination_pools::adapter::DirectStake; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index af8cca9bb091..75d6d728a512 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -158,3 +158,53 @@ pub trait StakingDelegationSupport { /// Reports an ongoing slash to the `delegate` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } + +/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. +pub trait PoolAdapter { + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + type AccountId: Clone + sp_std::fmt::Debug; + + /// Similar to [Inspect::balance]. + fn balance(who: &Self::AccountId) -> Self::Balance; + + /// Similar to [Inspect::total_balance]. + fn total_balance(who: &Self::AccountId) -> Self::Balance; + + /// Start delegation to the pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Add more delegation to the pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + /// + /// We need this along with [Self::delegate] as NominationPool has a slight different behaviour + /// for the first delegation and the subsequent ones. + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Revoke delegation to pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; +} From 13b4a8262e7ca0e381782f615884689fbc5908f5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 19 Feb 2024 09:39:53 +0100 Subject: [PATCH 110/202] rename DirectStake to NoDelegation to keep its name similar to Staking support impl --- .../frame/delegated-staking/src/impls.rs | 26 +++++++ .../frame/nomination-pools/src/adapter.rs | 77 +++++++++---------- substrate/frame/nomination-pools/src/mock.rs | 2 +- .../nomination-pools/test-staking/src/mock.rs | 2 +- 4 files changed, 66 insertions(+), 41 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 5db6bbb333ee..b779c7bf96a7 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -18,6 +18,7 @@ //! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. +use sp_staking::delegation::PoolAdapter; use super::*; /// StakingInterface implementation with delegation support. @@ -280,3 +281,28 @@ impl StakingDelegationSupport for Pallet { }); } } + +impl PoolAdapter for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn balance(who: &Self::AccountId) -> Self::Balance { + todo!() + } + + fn total_balance(who: &Self::AccountId) -> Self::Balance { + todo!() + } + + fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + todo!() + } + + fn delegate_extra(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + todo!() + } + + fn release_delegation(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + todo!() + } +} \ No newline at end of file diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 335a23850067..7562b11ff258 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -16,53 +16,52 @@ // limitations under the License. use crate::*; -use frame_support::traits::tokens::Balance; -/// Pool adapter that supports DirectStake. -pub struct DirectStake(PhantomData); +/// Basic pool adapter that only supports Direct Staking. +/// +/// When delegating, tokens are moved between the delegator and pool account as opposed to holding +/// tokens in delegator's accounts. +pub struct NoDelegation(PhantomData); -impl sp_staking::delegation::PoolAdapter for DirectStake { - type Balance = BalanceOf; - type AccountId = T::AccountId; +impl PoolAdapter for NoDelegation { + type Balance = BalanceOf; + type AccountId = T::AccountId; - fn balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::balance(who) - } + fn balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::balance(who) + } - fn total_balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::total_balance(who) - } + fn total_balance(who: &Self::AccountId) -> Self::Balance { + T::Currency::total_balance(who) + } - fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - T::Currency::transfer( - who, - &pool_account, - amount, - Preservation::Expendable, - )?; + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + T::Currency::transfer(who, &pool_account, amount, Preservation::Expendable)?; - Ok(()) - } + Ok(()) + } - fn delegate_extra(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - T::Currency::transfer( - who, - &pool_account, - amount, - Preservation::Preserve, - )?; + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + T::Currency::transfer(who, &pool_account, amount, Preservation::Preserve)?; - Ok(()) - } + Ok(()) + } - fn release_delegation(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - T::Currency::transfer( - &pool_account, - &who, - amount, - Preservation::Expendable, - )?; + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + T::Currency::transfer(&pool_account, &who, amount, Preservation::Expendable)?; - Ok(()) - } + Ok(()) + } } diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index b60f4a26b3ac..85b8c3b64405 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -311,7 +311,7 @@ impl pools::Config for Runtime { type MaxMetadataLen = MaxMetadataLen; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type PoolAdapter = adapter::DirectStake; + type PoolAdapter = adapter::NoDelegation; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 6b327ba5bb27..60b782d5b9ac 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -187,7 +187,7 @@ impl pallet_nomination_pools::Config for Runtime { type MaxUnbonding = ConstU32<8>; type MaxPointsToBalance = ConstU8<10>; type PalletId = PoolsPalletId; - type PoolAdapter = pallet_nomination_pools::adapter::DirectStake; + type PoolAdapter = pallet_nomination_pools::adapter::NoDelegation; } type Block = frame_system::mocking::MockBlock; From bed9c4cafc54a3cbe1d3acf59c80dd2197cb1a2f Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 19 Feb 2024 09:40:15 +0100 Subject: [PATCH 111/202] fmt --- .../frame/delegated-staking/src/impls.rs | 22 ++++++++++++++----- substrate/frame/delegated-staking/src/lib.rs | 1 - substrate/frame/nomination-pools/src/lib.rs | 7 +++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index b779c7bf96a7..34dbce708aa1 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -18,8 +18,8 @@ //! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. -use sp_staking::delegation::PoolAdapter; use super::*; +use sp_staking::delegation::PoolAdapter; /// StakingInterface implementation with delegation support. /// @@ -294,15 +294,27 @@ impl PoolAdapter for Pallet { todo!() } - fn delegate(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { todo!() } - fn delegate_extra(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { todo!() } - fn release_delegation(who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { todo!() } -} \ No newline at end of file +} diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 5527187f20ad..d0bdd67384e2 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -17,7 +17,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] - // FIXME(ank4n): fix docs //! An implementation of a delegation system for staking that can be utilised using //! [`DelegationInterface`]. In future, if exposed via extrinsic, these primitives could also be diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 4566494dfb56..d021961bced3 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,7 +373,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{EraIndex, StakingInterface, delegation::PoolAdapter}; +use sp_staking::{delegation::PoolAdapter, EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -397,9 +397,9 @@ pub mod mock; #[cfg(test)] mod tests; +pub mod adapter; pub mod migration; pub mod weights; -pub mod adapter; pub use pallet::*; pub use weights::WeightInfo; @@ -1271,7 +1271,8 @@ impl BondedPool { // found, we exit early. BondType::Later => { T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?; - T::Staking::bond_extra(&bonded_account, amount)? }, + T::Staking::bond_extra(&bonded_account, amount)? + }, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); From 54acfb71faf5ab80bdf46e97791d4f50a15423dc Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 19 Feb 2024 11:05:31 +0100 Subject: [PATCH 112/202] move adapter logic local to pool --- .../frame/delegated-staking/src/impls.rs | 38 --------- .../frame/nomination-pools/src/adapter.rs | 82 ++++++++++++++++++- substrate/frame/nomination-pools/src/lib.rs | 18 ++-- substrate/frame/nomination-pools/src/mock.rs | 2 +- .../nomination-pools/test-staking/src/mock.rs | 2 +- .../primitives/staking/src/delegation.rs | 50 ----------- 6 files changed, 93 insertions(+), 99 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 34dbce708aa1..5db6bbb333ee 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -19,7 +19,6 @@ //! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. use super::*; -use sp_staking::delegation::PoolAdapter; /// StakingInterface implementation with delegation support. /// @@ -281,40 +280,3 @@ impl StakingDelegationSupport for Pallet { }); } } - -impl PoolAdapter for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - fn balance(who: &Self::AccountId) -> Self::Balance { - todo!() - } - - fn total_balance(who: &Self::AccountId) -> Self::Balance { - todo!() - } - - fn delegate( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - todo!() - } - - fn delegate_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - todo!() - } - - fn release_delegation( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - todo!() - } -} diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 7562b11ff258..b569573819ac 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -16,6 +16,49 @@ // limitations under the License. use crate::*; +use frame_support::traits::tokens::Balance; + +/// Staking adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. +pub trait StakingAdapter { + type Balance: Balance; + type AccountId: Clone + Debug; + + /// Similar to [Inspect::balance]. + fn balance(who: &Self::AccountId) -> Self::Balance; + + /// Similar to [Inspect::total_balance]. + fn total_balance(who: &Self::AccountId) -> Self::Balance; + + /// Start delegation to the pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Add more delegation to the pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + /// + /// We need this along with [Self::delegate] as NominationPool has a slight different behaviour + /// for the first delegation and the subsequent ones. + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Revoke delegation to pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; +} /// Basic pool adapter that only supports Direct Staking. /// @@ -23,7 +66,7 @@ use crate::*; /// tokens in delegator's accounts. pub struct NoDelegation(PhantomData); -impl PoolAdapter for NoDelegation { +impl StakingAdapter for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -65,3 +108,40 @@ impl PoolAdapter for NoDelegation { Ok(()) } } + +impl StakingAdapter for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn balance(who: &Self::AccountId) -> Self::Balance { + todo!() + } + + fn total_balance(who: &Self::AccountId) -> Self::Balance { + todo!() + } + + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + todo!() + } + + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + todo!() + } + + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + todo!() + } +} diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index d021961bced3..3c823e3cdb56 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,7 +373,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{delegation::PoolAdapter, EraIndex, StakingInterface}; +use sp_staking::{EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -398,6 +398,8 @@ pub mod mock; mod tests; pub mod adapter; +use adapter::StakingAdapter; + pub mod migration; pub mod weights; @@ -1057,7 +1059,7 @@ impl BondedPool { // `pallet-nomination-pool`. This means reducible balance always returns balance preserving // ED in the account. What we want though is transferable balance given the account can be // dusted. - T::PoolAdapter::balance(&account) + T::StakingAdapter::balance(&account) .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) } @@ -1263,14 +1265,14 @@ impl BondedPool { match ty { BondType::Create => { - T::PoolAdapter::delegate(who, &bonded_account, amount)?; + T::StakingAdapter::delegate(who, &bonded_account, amount)?; T::Staking::bond(&bonded_account, amount, &self.reward_account())? }, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. BondType::Later => { - T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?; + T::StakingAdapter::delegate_extra(who, &bonded_account, amount)?; T::Staking::bond_extra(&bonded_account, amount)? }, } @@ -1654,7 +1656,7 @@ pub mod pallet { type MaxMetadataLen: Get; /// An adapter to support delegated to direct staking. - type PoolAdapter: PoolAdapter>; + type StakingAdapter: StakingAdapter>; } /// The sum of funds across all pools. @@ -2292,7 +2294,7 @@ pub mod pallet { // order to ensure members can leave the pool and it can be destroyed. .min(bonded_pool.transferable_balance()); - T::PoolAdapter::release_delegation( + T::StakingAdapter::release_delegation( &member_account, &bonded_pool.bonded_account(), balance_to_unbond, @@ -2877,7 +2879,7 @@ impl Pallet { "could not transfer all amount to depositor while dissolving pool" ); defensive_assert!( - T::PoolAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), + T::StakingAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), "dissolving pool should not have any balance" ); // NOTE: Defensively force set balance to zero. @@ -3459,7 +3461,7 @@ impl Pallet { let sum_unbonding_balance = subs.sum_unbonding_balance(); let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default(); - let total_balance = T::PoolAdapter::total_balance(&pool_account); + let total_balance = T::StakingAdapter::total_balance(&pool_account); assert!( total_balance >= bonded_balance + sum_unbonding_balance, diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 85b8c3b64405..8fb2bd89668e 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -311,7 +311,7 @@ impl pools::Config for Runtime { type MaxMetadataLen = MaxMetadataLen; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type PoolAdapter = adapter::NoDelegation; + type StakingAdapter = adapter::NoDelegation; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 60b782d5b9ac..30933171e605 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -187,7 +187,7 @@ impl pallet_nomination_pools::Config for Runtime { type MaxUnbonding = ConstU32<8>; type MaxPointsToBalance = ConstU8<10>; type PalletId = PoolsPalletId; - type PoolAdapter = pallet_nomination_pools::adapter::NoDelegation; + type StakingAdapter = pallet_nomination_pools::adapter::NoDelegation; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 75d6d728a512..af8cca9bb091 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -158,53 +158,3 @@ pub trait StakingDelegationSupport { /// Reports an ongoing slash to the `delegate` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } - -/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. -pub trait PoolAdapter { - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - type AccountId: Clone + sp_std::fmt::Debug; - - /// Similar to [Inspect::balance]. - fn balance(who: &Self::AccountId) -> Self::Balance; - - /// Similar to [Inspect::total_balance]. - fn total_balance(who: &Self::AccountId) -> Self::Balance; - - /// Start delegation to the pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. - fn delegate( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Add more delegation to the pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. - /// - /// We need this along with [Self::delegate] as NominationPool has a slight different behaviour - /// for the first delegation and the subsequent ones. - fn delegate_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Revoke delegation to pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. - fn release_delegation( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; -} From ea78f168d02632c887af6a187cf8730d83c6a1f4 Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 19 Feb 2024 11:17:56 +0100 Subject: [PATCH 113/202] Revert "move adapter logic local to pool" This reverts commit 54acfb71faf5ab80bdf46e97791d4f50a15423dc. --- .../frame/delegated-staking/src/impls.rs | 38 +++++++++ .../frame/nomination-pools/src/adapter.rs | 82 +------------------ substrate/frame/nomination-pools/src/lib.rs | 18 ++-- substrate/frame/nomination-pools/src/mock.rs | 2 +- .../nomination-pools/test-staking/src/mock.rs | 2 +- .../primitives/staking/src/delegation.rs | 50 +++++++++++ 6 files changed, 99 insertions(+), 93 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 5db6bbb333ee..34dbce708aa1 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -19,6 +19,7 @@ //! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. use super::*; +use sp_staking::delegation::PoolAdapter; /// StakingInterface implementation with delegation support. /// @@ -280,3 +281,40 @@ impl StakingDelegationSupport for Pallet { }); } } + +impl PoolAdapter for Pallet { + type Balance = BalanceOf; + type AccountId = T::AccountId; + + fn balance(who: &Self::AccountId) -> Self::Balance { + todo!() + } + + fn total_balance(who: &Self::AccountId) -> Self::Balance { + todo!() + } + + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + todo!() + } + + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + todo!() + } + + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + todo!() + } +} diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index b569573819ac..7562b11ff258 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -16,49 +16,6 @@ // limitations under the License. use crate::*; -use frame_support::traits::tokens::Balance; - -/// Staking adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. -pub trait StakingAdapter { - type Balance: Balance; - type AccountId: Clone + Debug; - - /// Similar to [Inspect::balance]. - fn balance(who: &Self::AccountId) -> Self::Balance; - - /// Similar to [Inspect::total_balance]. - fn total_balance(who: &Self::AccountId) -> Self::Balance; - - /// Start delegation to the pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. - fn delegate( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Add more delegation to the pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. - /// - /// We need this along with [Self::delegate] as NominationPool has a slight different behaviour - /// for the first delegation and the subsequent ones. - fn delegate_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Revoke delegation to pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. - fn release_delegation( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; -} /// Basic pool adapter that only supports Direct Staking. /// @@ -66,7 +23,7 @@ pub trait StakingAdapter { /// tokens in delegator's accounts. pub struct NoDelegation(PhantomData); -impl StakingAdapter for NoDelegation { +impl PoolAdapter for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -108,40 +65,3 @@ impl StakingAdapter for NoDelegation { Ok(()) } } - -impl StakingAdapter for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - fn balance(who: &Self::AccountId) -> Self::Balance { - todo!() - } - - fn total_balance(who: &Self::AccountId) -> Self::Balance { - todo!() - } - - fn delegate( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - todo!() - } - - fn delegate_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - todo!() - } - - fn release_delegation( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - todo!() - } -} diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 3c823e3cdb56..d021961bced3 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,7 +373,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{EraIndex, StakingInterface}; +use sp_staking::{delegation::PoolAdapter, EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -398,8 +398,6 @@ pub mod mock; mod tests; pub mod adapter; -use adapter::StakingAdapter; - pub mod migration; pub mod weights; @@ -1059,7 +1057,7 @@ impl BondedPool { // `pallet-nomination-pool`. This means reducible balance always returns balance preserving // ED in the account. What we want though is transferable balance given the account can be // dusted. - T::StakingAdapter::balance(&account) + T::PoolAdapter::balance(&account) .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) } @@ -1265,14 +1263,14 @@ impl BondedPool { match ty { BondType::Create => { - T::StakingAdapter::delegate(who, &bonded_account, amount)?; + T::PoolAdapter::delegate(who, &bonded_account, amount)?; T::Staking::bond(&bonded_account, amount, &self.reward_account())? }, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. BondType::Later => { - T::StakingAdapter::delegate_extra(who, &bonded_account, amount)?; + T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?; T::Staking::bond_extra(&bonded_account, amount)? }, } @@ -1656,7 +1654,7 @@ pub mod pallet { type MaxMetadataLen: Get; /// An adapter to support delegated to direct staking. - type StakingAdapter: StakingAdapter>; + type PoolAdapter: PoolAdapter>; } /// The sum of funds across all pools. @@ -2294,7 +2292,7 @@ pub mod pallet { // order to ensure members can leave the pool and it can be destroyed. .min(bonded_pool.transferable_balance()); - T::StakingAdapter::release_delegation( + T::PoolAdapter::release_delegation( &member_account, &bonded_pool.bonded_account(), balance_to_unbond, @@ -2879,7 +2877,7 @@ impl Pallet { "could not transfer all amount to depositor while dissolving pool" ); defensive_assert!( - T::StakingAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), + T::PoolAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), "dissolving pool should not have any balance" ); // NOTE: Defensively force set balance to zero. @@ -3461,7 +3459,7 @@ impl Pallet { let sum_unbonding_balance = subs.sum_unbonding_balance(); let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default(); - let total_balance = T::StakingAdapter::total_balance(&pool_account); + let total_balance = T::PoolAdapter::total_balance(&pool_account); assert!( total_balance >= bonded_balance + sum_unbonding_balance, diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 8fb2bd89668e..85b8c3b64405 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -311,7 +311,7 @@ impl pools::Config for Runtime { type MaxMetadataLen = MaxMetadataLen; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type StakingAdapter = adapter::NoDelegation; + type PoolAdapter = adapter::NoDelegation; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 30933171e605..60b782d5b9ac 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -187,7 +187,7 @@ impl pallet_nomination_pools::Config for Runtime { type MaxUnbonding = ConstU32<8>; type MaxPointsToBalance = ConstU8<10>; type PalletId = PoolsPalletId; - type StakingAdapter = pallet_nomination_pools::adapter::NoDelegation; + type PoolAdapter = pallet_nomination_pools::adapter::NoDelegation; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index af8cca9bb091..75d6d728a512 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -158,3 +158,53 @@ pub trait StakingDelegationSupport { /// Reports an ongoing slash to the `delegate` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } + +/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. +pub trait PoolAdapter { + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; + type AccountId: Clone + sp_std::fmt::Debug; + + /// Similar to [Inspect::balance]. + fn balance(who: &Self::AccountId) -> Self::Balance; + + /// Similar to [Inspect::total_balance]. + fn total_balance(who: &Self::AccountId) -> Self::Balance; + + /// Start delegation to the pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Add more delegation to the pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake. + /// + /// We need this along with [Self::delegate] as NominationPool has a slight different behaviour + /// for the first delegation and the subsequent ones. + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Revoke delegation to pool account. + /// + /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. + fn release_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; +} From a32444252ae9b095adfe4348701872c2193811e2 Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 19 Feb 2024 11:40:45 +0100 Subject: [PATCH 114/202] quick basic impl of Delegation adapter for pool --- .../frame/delegated-staking/src/impls.rs | 24 +++++++++++++++---- .../frame/delegated-staking/src/types.rs | 19 ++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 34dbce708aa1..67e42e6f3dce 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -286,20 +286,33 @@ impl PoolAdapter for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; + /// Return balance of the `Delegate` (pool account) that is not bonded. + /// + /// Equivalent to [FunInspect::balance] for non delegate accounts. fn balance(who: &Self::AccountId) -> Self::Balance { - todo!() + Delegate::::from(who) + .map(|delegate| delegate.unbonded()) + .unwrap_or(Zero::zero()) } + /// Returns balance of `Delegate` account including the held balances. + /// + /// Equivalent to [FunInspect::total_balance] for non delegate accounts. fn total_balance(who: &Self::AccountId) -> Self::Balance { - todo!() + Delegate::::from(who) + .map(|delegate| delegate.ledger.effective_balance()) + .unwrap_or(Zero::zero()) } + /// Add initial delegation to the pool account. + /// + /// Equivalent to [FunMutate::transfer] for Direct Staking. fn delegate( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - todo!() + Pallet::::delegate_funds(RawOrigin::Signed(who.clone()).into(), pool_account.clone(), amount) } fn delegate_extra( @@ -307,7 +320,7 @@ impl PoolAdapter for Pallet { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - todo!() + Self::delegate(who, pool_account, amount) } fn release_delegation( @@ -315,6 +328,7 @@ impl PoolAdapter for Pallet { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - todo!() + // fixme(ank4n): This should not require slashing spans. + Pallet::::release(RawOrigin::Signed(pool_account.clone()).into(), who.clone(), amount, 0) } } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 5999d4b9a977..5de79e0fb228 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -159,14 +159,27 @@ impl Delegate { } pub(crate) fn available_to_bond(&self) -> BalanceOf { - let exposed_stake = self.bonded_stake(); + let bonded_stake = self.bonded_stake(); let stakeable = self.ledger().map(|ledger| ledger.stakeable_balance()).unwrap_or(Zero::zero()); - defensive_assert!(stakeable >= exposed_stake, "cannot expose more than delegate balance"); + defensive_assert!(stakeable >= bonded_stake, "cannot expose more than delegate balance"); - stakeable.saturating_sub(exposed_stake) + stakeable.saturating_sub(bonded_stake) + } + + /// Similar to [`Self::available_to_bond`] but includes `DelegationLedger.unclaimed_withdrawals` + /// as well. + pub(crate) fn unbonded(&self) -> BalanceOf { + let bonded_stake = self.bonded_stake(); + + let net_balance = + self.ledger().map(|ledger| ledger.effective_balance()).unwrap_or(Zero::zero()); + + defensive_assert!(net_balance >= bonded_stake, "cannot expose more than delegate balance"); + + net_balance.saturating_sub(bonded_stake) } pub(crate) fn ledger(&self) -> Option> { From 10fadcdbee8fe7dd4acb3d58c90bf848a8c52a15 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 21 Feb 2024 15:34:52 +0100 Subject: [PATCH 115/202] add np to delegate pallet mock runtime --- Cargo.lock | 1 + substrate/frame/delegated-staking/Cargo.toml | 1 + substrate/frame/delegated-staking/src/mock.rs | 43 +++++++++++++++++-- substrate/frame/nomination-pools/src/lib.rs | 2 +- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09a70664d1fd..1277f68730cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9767,6 +9767,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", + "pallet-nomination-pools", "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", diff --git a/substrate/frame/delegated-staking/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml index 735b2794d5ea..4b278af64cf3 100644 --- a/substrate/frame/delegated-staking/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -26,6 +26,7 @@ sp-io = { path = "../../primitives/io" } substrate-test-utils = { path = "../../test-utils" } sp-tracing = { path = "../../primitives/tracing" } pallet-staking = { path = "../staking" } +pallet-nomination-pools = { path = "../nomination-pools" } pallet-balances = { path = "../balances" } pallet-timestamp = { path = "../timestamp" } pallet-staking-reward-curve = { path = "../staking/reward-curve" } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index e001444b5fce..ad69680ecdc6 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -32,6 +32,8 @@ use frame_election_provider_support::{ }; use frame_support::dispatch::RawOrigin; use pallet_staking::CurrentEra; +use sp_core::U256; +use sp_runtime::traits::Convert; use sp_staking::{delegation::StakingDelegationSupport, Stake, StakingInterface}; pub type T = Runtime; @@ -72,10 +74,10 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = (); + type RuntimeFreezeReason = RuntimeFreezeReason; } pallet_staking_reward_curve::build! { @@ -148,6 +150,40 @@ impl delegated_staking::Config for Runtime { type CoreStaking = Staking; } +pub struct BalanceToU256; +impl Convert for BalanceToU256 { + fn convert(n: Balance) -> U256 { + n.into() + } +} +pub struct U256ToBalance; +impl Convert for U256ToBalance { + fn convert(n: U256) -> Balance { + n.try_into().unwrap() + } +} + +parameter_types! { + pub static MaxUnbonding: u32 = 8; + pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); +} +impl pallet_nomination_pools::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; + type RuntimeFreezeReason = RuntimeFreezeReason; + type RewardCounter = sp_runtime::FixedU128; + type BalanceToU256 = BalanceToU256; + type U256ToBalance = U256ToBalance; + type Staking = DelegatedStaking; + type PostUnbondingPoolsWindow = ConstU32<2>; + type PalletId = PoolsPalletId; + type MaxMetadataLen = ConstU32<256>; + type MaxUnbonding = MaxUnbonding; + type MaxPointsToBalance = frame_support::traits::ConstU8<10>; + type PoolAdapter = DelegatedStaking; +} + frame_support::construct_runtime!( pub enum Runtime { System: frame_system, @@ -155,6 +191,7 @@ frame_support::construct_runtime!( Balances: pallet_balances, Staking: pallet_staking, DelegatedStaking: delegated_staking, + NominationPools: pallet_nomination_pools, } ); diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index d021961bced3..d75fc774d390 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -3117,7 +3117,7 @@ impl Pallet { ) -> DispatchResult { if signer != member_account { ensure!( - ClaimPermissions::::get(&member_account).can_bond_extra(), + ClaimPermissions::::get(&member_account).can_bond_extra(), Error::::DoesNotHavePermission ); ensure!(extra == BondExtra::Rewards, Error::::BondExtraRestricted); From 9cafa0e6a05218b9fdb31ec7fd3ed2095433e993 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 21 Feb 2024 15:39:07 +0100 Subject: [PATCH 116/202] fmt --- substrate/frame/delegated-staking/src/impls.rs | 6 +++++- substrate/frame/nomination-pools/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 67e42e6f3dce..5ee361ffa443 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -312,7 +312,11 @@ impl PoolAdapter for Pallet { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - Pallet::::delegate_funds(RawOrigin::Signed(who.clone()).into(), pool_account.clone(), amount) + Pallet::::delegate_funds( + RawOrigin::Signed(who.clone()).into(), + pool_account.clone(), + amount, + ) } fn delegate_extra( diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index d75fc774d390..d021961bced3 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -3117,7 +3117,7 @@ impl Pallet { ) -> DispatchResult { if signer != member_account { ensure!( - ClaimPermissions::::get(&member_account).can_bond_extra(), + ClaimPermissions::::get(&member_account).can_bond_extra(), Error::::DoesNotHavePermission ); ensure!(extra == BondExtra::Rewards, Error::::BondExtraRestricted); From f10e589bee8d68fac2e1b671e135eb04e21e32b6 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 21 Feb 2024 15:53:58 +0100 Subject: [PATCH 117/202] tests skeleton --- .../frame/delegated-staking/src/tests.rs | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 69264bfec036..10324502c0b0 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -202,7 +202,7 @@ fn apply_pending_slash() { } /// Integration tests with pallet-staking. -mod integration { +mod staking_integration { use super::*; use pallet_staking::RewardDestination; use sp_staking::Stake; @@ -586,3 +586,66 @@ mod integration { }); } } + +mod pool_integration { + + #[test] + fn create_pool() { + + } + + #[test] + fn join_pool() { + + } + + #[test] + fn bond_extra_to_pool() { + + } + + #[test] + fn claim_pool_rewards() { + + } + + #[test] + fn unbond_delegation_from_pool() { + + } + + #[test] + fn pool_withdraw_unbonded() { + + } + + #[test] + fn delegator_withdraw_unbonded() { + + } + + #[test] + fn update_nominations() { + + } + + #[test] + fn destroy_pool() { + + } + + #[test] + fn chill_pool() { + + } + + #[test] + fn claim_commission_pool_operator() { + + } + + #[test] + fn pool_slashed() { + + } +} From b11ea7a8b941ea66adf598659485759213099891 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 21 Feb 2024 17:38:05 +0100 Subject: [PATCH 118/202] use balance type u128 so np pot accounts are unique --- .../frame/delegated-staking/src/impls.rs | 9 ++++++++- substrate/frame/delegated-staking/src/mock.rs | 16 +++++++++------ .../frame/delegated-staking/src/tests.rs | 20 ++++++++++++++++++- .../frame/nomination-pools/src/adapter.rs | 9 ++++----- substrate/frame/nomination-pools/src/lib.rs | 12 ++++------- .../primitives/staking/src/delegation.rs | 10 ++-------- 6 files changed, 47 insertions(+), 29 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 5ee361ffa443..1565d0a5ffdc 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -310,8 +310,11 @@ impl PoolAdapter for Pallet { fn delegate( who: &Self::AccountId, pool_account: &Self::AccountId, + reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { + Pallet::::register_as_delegate(RawOrigin::Signed(pool_account.clone()).into(), reward_account.clone())?; + Pallet::::delegate_funds( RawOrigin::Signed(who.clone()).into(), pool_account.clone(), @@ -324,7 +327,11 @@ impl PoolAdapter for Pallet { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - Self::delegate(who, pool_account, amount) + Pallet::::delegate_funds( + RawOrigin::Signed(who.clone()).into(), + pool_account.clone(), + amount, + ) } fn release_delegation( diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index ad69680ecdc6..26a3556e9f6e 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -15,12 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{self as delegated_staking, types::Delegate}; +use crate::{self as delegated_staking, types::Delegate, HoldReason}; use frame_support::{ - assert_ok, derive_impl, + assert_noop, assert_ok, derive_impl, pallet_prelude::*, parameter_types, - traits::{ConstU64, Currency}, + traits::{fungible::InspectHold, ConstU64, Currency}, PalletId, }; @@ -38,7 +38,7 @@ use sp_staking::{delegation::StakingDelegationSupport, Stake, StakingInterface}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; -pub type AccountId = u64; +pub type AccountId = u128; pub const GENESIS_VALIDATOR: AccountId = 1; pub const GENESIS_NOMINATOR_ONE: AccountId = 101; @@ -99,7 +99,7 @@ parameter_types! { pub struct OnChainSeqPhragmen; impl onchain::Config for OnChainSeqPhragmen { type System = Runtime; - type Solver = SequentialPhragmen; + type Solver = SequentialPhragmen; type DataProvider = Staking; type WeightInfo = (); type MaxWinners = ConstU32<100>; @@ -191,7 +191,7 @@ frame_support::construct_runtime!( Balances: pallet_balances, Staking: pallet_staking, DelegatedStaking: delegated_staking, - NominationPools: pallet_nomination_pools, + Pools: pallet_nomination_pools, } ); @@ -333,3 +333,7 @@ pub(crate) fn delegate_bonded(delegate: &AccountId) -> bool { pub(crate) fn get_delegate(delegate: &AccountId) -> Delegate { Delegate::::from(delegate).expect("delegate should exist") } + +pub(crate) fn held_balance(who: &AccountId) -> Balance { + Balances::balance_on_hold(&HoldReason::Delegating.into(), &who) +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 10324502c0b0..3cfc3de3954c 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -588,15 +588,33 @@ mod staking_integration { } mod pool_integration { - + use super::*; #[test] fn create_pool() { + ExtBuilder::default().build_and_execute(|| { + let creator: AccountId = 100; + fund(&creator, 500); + let delegate_amount = 200; + + assert_ok!(Pools::create(RawOrigin::Signed(creator).into(), delegate_amount, creator, creator, creator)); + assert_eq!(held_balance(&creator), delegate_amount); + let pool_account = Pools::create_bonded_account(1); + let delegate = get_delegate(&pool_account); + }); } #[test] fn join_pool() { + for i in 1..10000000 { + let bonded = Pools::create_bonded_account(i); + let reward = Pools::create_reward_account(i); + if bonded != reward { + println!("Index: {:?}, Bonded: {:?}, Reward: {:?}",i, bonded, reward); + break + } + } } #[test] diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 7562b11ff258..bc33e32f80f2 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -38,11 +38,11 @@ impl PoolAdapter for NoDelegation { fn delegate( who: &Self::AccountId, pool_account: &Self::AccountId, + reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { T::Currency::transfer(who, &pool_account, amount, Preservation::Expendable)?; - - Ok(()) + T::Staking::bond(pool_account, amount, reward_account) } fn delegate_extra( @@ -50,9 +50,8 @@ impl PoolAdapter for NoDelegation { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - T::Currency::transfer(who, &pool_account, amount, Preservation::Preserve)?; - - Ok(()) + T::Currency::transfer(who, pool_account, amount, Preservation::Preserve)?; + T::Staking::bond_extra(pool_account, amount) } fn release_delegation( diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index d021961bced3..579b47051475 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1260,19 +1260,15 @@ impl BondedPool { // We must calculate the points issued *before* we bond who's funds, else points:balance // ratio will be wrong. let points_issued = self.issue(amount); + let reward_account = self.reward_account(); match ty { - BondType::Create => { - T::PoolAdapter::delegate(who, &bonded_account, amount)?; - T::Staking::bond(&bonded_account, amount, &self.reward_account())? - }, + BondType::Create => + T::PoolAdapter::delegate(who, &bonded_account, &reward_account, amount)?, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. - BondType::Later => { - T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?; - T::Staking::bond_extra(&bonded_account, amount)? - }, + BondType::Later => T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 75d6d728a512..e1d1120684fe 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -178,21 +178,15 @@ pub trait PoolAdapter { /// Similar to [Inspect::total_balance]. fn total_balance(who: &Self::AccountId) -> Self::Balance; - /// Start delegation to the pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. + /// Initiate delegation to the pool account. fn delegate( who: &Self::AccountId, pool_account: &Self::AccountId, + reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; /// Add more delegation to the pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake. - /// - /// We need this along with [Self::delegate] as NominationPool has a slight different behaviour - /// for the first delegation and the subsequent ones. fn delegate_extra( who: &Self::AccountId, pool_account: &Self::AccountId, From 8029e2e8f3fa4294a313f54e22d2657df60db353 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 21 Feb 2024 17:46:31 +0100 Subject: [PATCH 119/202] create pool --- .../frame/delegated-staking/src/impls.rs | 17 ++++++-- .../frame/delegated-staking/src/tests.rs | 42 ++++++++++--------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 1565d0a5ffdc..f4c3e455ae48 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -313,13 +313,21 @@ impl PoolAdapter for Pallet { reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - Pallet::::register_as_delegate(RawOrigin::Signed(pool_account.clone()).into(), reward_account.clone())?; + // First delegation so we needs to register the pool account as delegate. + Pallet::::register_as_delegate( + RawOrigin::Signed(pool_account.clone()).into(), + reward_account.clone(), + )?; + // Delegate the funds from who to the pool account. Pallet::::delegate_funds( RawOrigin::Signed(who.clone()).into(), pool_account.clone(), amount, - ) + )?; + + // Bond the funds to staking. + Pallet::::bond(pool_account, amount, reward_account) } fn delegate_extra( @@ -331,7 +339,10 @@ impl PoolAdapter for Pallet { RawOrigin::Signed(who.clone()).into(), pool_account.clone(), amount, - ) + )?; + + // Bond the funds to staking. + Pallet::::bond_extra(pool_account, amount) } fn release_delegation( diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 3cfc3de3954c..812770a2b2bd 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -596,74 +596,76 @@ mod pool_integration { fund(&creator, 500); let delegate_amount = 200; - assert_ok!(Pools::create(RawOrigin::Signed(creator).into(), delegate_amount, creator, creator, creator)); + assert_ok!(Pools::create( + RawOrigin::Signed(creator).into(), + delegate_amount, + creator, + creator, + creator + )); assert_eq!(held_balance(&creator), delegate_amount); let pool_account = Pools::create_bonded_account(1); let delegate = get_delegate(&pool_account); + + assert_eq!(delegate.ledger.effective_balance(), delegate_amount); + assert_eq!(delegate.available_to_bond(), 0); + assert_eq!(delegate.unbonded(), 0); }); } #[test] fn join_pool() { - for i in 1..10000000 { - let bonded = Pools::create_bonded_account(i); - let reward = Pools::create_reward_account(i); - - if bonded != reward { - println!("Index: {:?}, Bonded: {:?}, Reward: {:?}",i, bonded, reward); - break - } - } + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn bond_extra_to_pool() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn claim_pool_rewards() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn unbond_delegation_from_pool() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn pool_withdraw_unbonded() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn delegator_withdraw_unbonded() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn update_nominations() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn destroy_pool() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn chill_pool() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn claim_commission_pool_operator() { - + ExtBuilder::default().build_and_execute(|| {}); } #[test] fn pool_slashed() { - + ExtBuilder::default().build_and_execute(|| {}); } } From f283207da68f291b779edc91d950145efc240092 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 21 Feb 2024 18:04:39 +0100 Subject: [PATCH 120/202] increment provider on pool account --- substrate/frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index f4c3e455ae48..e6c86968287a 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -313,7 +313,7 @@ impl PoolAdapter for Pallet { reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - // First delegation so we needs to register the pool account as delegate. + // This is the first delegation so we needs to register the pool account as a `delegate`. Pallet::::register_as_delegate( RawOrigin::Signed(pool_account.clone()).into(), reward_account.clone(), diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d0bdd67384e2..0942641e52ee 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -218,8 +218,7 @@ pub mod pallet { // Reward account cannot be same as `delegate` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); - DelegationLedger::::new(&reward_account).save(&who); - + Self::do_register_delegator(&who, &reward_account); Ok(()) } @@ -374,6 +373,15 @@ impl Pallet { .unwrap_or(false) } + fn do_register_delegator(who: &T::AccountId, reward_account: &T::AccountId) { + DelegationLedger::::new(reward_account).save(who); + + // Pool account is a virtual account. Make this account exist. + // TODO: Someday if we expose these calls in a runtime, we should take a deposit for + // being a delegator. + frame_system::Pallet::::inc_providers(who); + } + fn do_migrate_to_delegate(who: &T::AccountId, reward_account: &T::AccountId) -> DispatchResult { // We create a proxy delegator that will keep all the delegation funds until funds are // transferred to actual delegator. @@ -399,7 +407,7 @@ impl Pallet { T::Currency::transfer(who, &proxy_delegator, stake.total, Preservation::Protect) .map_err(|_| Error::::BadState)?; - DelegationLedger::::new(&reward_account).save(&who); + Self::do_register_delegator(who, reward_account); // FIXME(ank4n) expose set payee in staking interface. // T::CoreStaking::set_payee(who, reward_account) From a30d3c3efb54093e15d01b39b64410cde7964571 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 00:12:42 +0100 Subject: [PATCH 121/202] join pool works --- .../frame/delegated-staking/src/impls.rs | 2 +- .../frame/delegated-staking/src/tests.rs | 63 ++++++++++++++++++- .../frame/delegated-staking/src/types.rs | 5 ++ 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index e6c86968287a..21aa212a22e9 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -65,7 +65,7 @@ impl StakingInterface for Pallet { if Self::is_delegator(who) { let delegation = Delegation::::get(who).defensive_ok_or(Error::::BadState)?; - return Ok(delegation.amount) + return Ok(delegation.amount); } Err(Error::::NotSupported.into()) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 812770a2b2bd..9d2716891aa6 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -590,12 +590,16 @@ mod staking_integration { mod pool_integration { use super::*; #[test] - fn create_pool() { + fn create_pool_test() { ExtBuilder::default().build_and_execute(|| { let creator: AccountId = 100; fund(&creator, 500); let delegate_amount = 200; + // nothing held initially + assert_eq!(held_balance(&creator), 0); + + // create pool assert_ok!(Pools::create( RawOrigin::Signed(creator).into(), delegate_amount, @@ -603,11 +607,14 @@ mod pool_integration { creator, creator )); + + // correct amount is locked in depositor's account. assert_eq!(held_balance(&creator), delegate_amount); let pool_account = Pools::create_bonded_account(1); let delegate = get_delegate(&pool_account); + // verify state assert_eq!(delegate.ledger.effective_balance(), delegate_amount); assert_eq!(delegate.available_to_bond(), 0); assert_eq!(delegate.unbonded(), 0); @@ -616,7 +623,48 @@ mod pool_integration { #[test] fn join_pool() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + // create a pool + create_pool(100, 200); + // keep track of staked amount. + let mut staked_amount: Balance = 200; + + // fund delegator + let delegator: AccountId = 300; + fund(&delegator, 500); + // nothing held initially + assert_eq!(held_balance(&delegator), 0); + + // delegator joins pool + assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), 100, 1)); + staked_amount += 100; + + // correct amount is locked in depositor's account. + assert_eq!(held_balance(&delegator), 100); + + // delegator is not actively exposed to core staking. + assert_eq!(Staking::status(&delegator), Err(StakingError::::NotStash.into())); + + let pool_delegate = get_delegate(&Pools::create_bonded_account(1)); + // verify state + assert_eq!(pool_delegate.ledger.effective_balance(), staked_amount); + assert_eq!(pool_delegate.bonded_stake(), staked_amount); + assert_eq!(pool_delegate.available_to_bond(), 0); + assert_eq!(pool_delegate.unbonded(), 0); + + // let a bunch of delegators join this pool + for i in 301..350 { + fund(&i, 500); + assert_ok!(Pools::join(RawOrigin::Signed(i).into(), (100 + i).into(), 1)); + staked_amount += 100 + i; + assert_eq!(held_balance(&i), 100 + i); + } + + assert_eq!(pool_delegate.refresh().unwrap().ledger.effective_balance(), staked_amount); + assert_eq!(pool_delegate.bonded_stake(), staked_amount); + assert_eq!(pool_delegate.available_to_bond(), 0); + assert_eq!(pool_delegate.unbonded(), 0); + }); } #[test] @@ -668,4 +716,15 @@ mod pool_integration { fn pool_slashed() { ExtBuilder::default().build_and_execute(|| {}); } + + fn create_pool(creator: AccountId, amount: Balance) { + fund(&creator, amount * 2); + assert_ok!(Pools::create( + RawOrigin::Signed(creator).into(), + amount, + creator, + creator, + creator + )); + } } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 5de79e0fb228..05de7ad21274 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -158,6 +158,11 @@ impl Delegate { Ok(Delegate { key: delegate.clone(), ledger }) } + // re-reads the delegate from database and returns a new instance. + pub(crate) fn refresh(&self) -> Result, DispatchError> { + Self::from(&self.key) + } + pub(crate) fn available_to_bond(&self) -> BalanceOf { let bonded_stake = self.bonded_stake(); From 7e09ebed21f72181e200de5b8bc80ffd3ea4911f Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 00:40:14 +0100 Subject: [PATCH 122/202] get actual pool id --- .../frame/delegated-staking/src/tests.rs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 9d2716891aa6..3e452c575f89 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -625,7 +625,7 @@ mod pool_integration { fn join_pool() { ExtBuilder::default().build_and_execute(|| { // create a pool - create_pool(100, 200); + let pool_id = create_pool(100, 200); // keep track of staked amount. let mut staked_amount: Balance = 200; @@ -636,7 +636,7 @@ mod pool_integration { assert_eq!(held_balance(&delegator), 0); // delegator joins pool - assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), 100, 1)); + assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), 100, pool_id)); staked_amount += 100; // correct amount is locked in depositor's account. @@ -655,7 +655,7 @@ mod pool_integration { // let a bunch of delegators join this pool for i in 301..350 { fund(&i, 500); - assert_ok!(Pools::join(RawOrigin::Signed(i).into(), (100 + i).into(), 1)); + assert_ok!(Pools::join(RawOrigin::Signed(i).into(), (100 + i).into(), pool_id)); staked_amount += 100 + i; assert_eq!(held_balance(&i), 100 + i); } @@ -669,7 +669,10 @@ mod pool_integration { #[test] fn bond_extra_to_pool() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + let pool_id = create_pool(100, 200); + add_delegators(pool_id, 100, (300..350).collect(), 100); + }); } #[test] @@ -717,7 +720,7 @@ mod pool_integration { ExtBuilder::default().build_and_execute(|| {}); } - fn create_pool(creator: AccountId, amount: Balance) { + fn create_pool(creator: AccountId, amount: Balance) -> u32 { fund(&creator, amount * 2); assert_ok!(Pools::create( RawOrigin::Signed(creator).into(), @@ -726,5 +729,14 @@ mod pool_integration { creator, creator )); + + pallet_nomination_pools::LastPoolId::::get() + } + + fn add_delegators(pool_id: u32, creator: AccountId, delegators: Vec, amount: Balance) { + for delegator in delegators { + fund(&delegator, amount * 2); + assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), creator, pool_id)); + } } } From e9819949f74790eb6b22e5852c9954803d49bd79 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 00:53:16 +0100 Subject: [PATCH 123/202] bond extra test --- substrate/frame/delegated-staking/src/tests.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 3e452c575f89..5f07e337460e 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -588,6 +588,7 @@ mod staking_integration { } mod pool_integration { + use pallet_nomination_pools::BondExtra; use super::*; #[test] fn create_pool_test() { @@ -671,7 +672,16 @@ mod pool_integration { fn bond_extra_to_pool() { ExtBuilder::default().build_and_execute(|| { let pool_id = create_pool(100, 200); - add_delegators(pool_id, 100, (300..350).collect(), 100); + add_delegators(pool_id, 100, (300..310).collect(), 100); + let mut staked_amount = 200 + 100 * 10; + assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); + + // bond extra to pool + for i in 300..310 { + assert_ok!(Pools::bond_extra(RawOrigin::Signed(i).into(), BondExtra::FreeBalance(50))); + staked_amount += 50; + assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); + } }); } @@ -739,4 +749,8 @@ mod pool_integration { assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), creator, pool_id)); } } + + fn get_pool_delegate(pool_id: u32) -> Delegate { + get_delegate(&Pools::create_bonded_account(pool_id)) + } } From 8b0d4ae327cfe03edbc7f77698aa601b5053a4a2 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 09:29:11 +0100 Subject: [PATCH 124/202] fix bug of adding surplus amount to delegate --- substrate/frame/delegated-staking/src/lib.rs | 74 ++++++++++++++++++- .../frame/delegated-staking/src/types.rs | 2 +- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 0942641e52ee..4a60f254a72e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -80,7 +80,7 @@ use frame_support::{ use sp_runtime::{ traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, - ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, + ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, TryRuntimeError, }; use sp_staking::{ delegation::{DelegationInterface, StakingDelegationSupport}, @@ -173,7 +173,7 @@ pub mod pallet { // } #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { Delegated { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, Withdrawn { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, @@ -339,6 +339,16 @@ pub mod pallet { Ok(()) } } + + #[pallet::hooks] + impl Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), TryRuntimeError> { + Self::do_try_state() + } + + fn integrity_test() {} + } } impl Pallet { @@ -450,7 +460,7 @@ impl Pallet { ledger.total_delegated = ledger .total_delegated - .checked_add(&new_delegation_amount) + .checked_add(&amount) .ok_or(ArithmeticError::Overflow)?; ledger.save(delegate); @@ -610,9 +620,65 @@ impl Pallet { } } +#[cfg(any(test, feature = "try-runtime"))] +use sp_std::collections::btree_map::BTreeMap; #[cfg(any(test, feature = "try-runtime"))] impl Pallet { - pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + pub(crate) fn do_try_state() -> Result<(), TryRuntimeError> { + + // build map to avoid reading storage multiple times. + let delegation_map = Delegators::::iter().collect::>(); + let ledger_map = Delegates::::iter().collect::>(); + + Self::check_delegates(ledger_map.clone())?; + Self::check_delegators(delegation_map, ledger_map)?; + + Ok(()) + } + + fn check_delegates( + ledgers: BTreeMap>, + ) -> Result<(), TryRuntimeError> { + for (delegate, ledger) in ledgers { + ensure!( + matches!( + T::CoreStaking::status(&delegate).expect("delegate should be bonded"), + StakerStatus::Nominator(_) | StakerStatus::Idle + ), + "delegate should be bonded and not validator" + ); + + ensure!( + ledger.stakeable_balance() >= + T::CoreStaking::total_stake(&delegate) + .expect("delegate should exist as a nominator"), + "all delegated balance is staked" + ); + } + + Ok(()) + } + + fn check_delegators( + delegations: BTreeMap>, + ledger: BTreeMap>, + ) -> Result<(), TryRuntimeError> { + let mut delegation_aggregation = BTreeMap::>::new(); + delegations.iter().for_each(|(delegator, delegation)| { + delegation_aggregation + .entry(delegation.delegate.clone()) + .and_modify(|e| *e += delegation.amount) + .or_insert(delegation.amount); + }); + + for (delegate, total_delegated) in delegation_aggregation { + let ledger = ledger.get(&delegate).expect("ledger should exist"); + ensure!( + ledger.total_delegated == total_delegated, + "ledger total delegated should match delegations" + ); + } + Ok(()) } } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 05de7ad21274..1a90d762dcfd 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -31,7 +31,7 @@ pub(crate) enum AccountType { ProxyDelegator, } -#[derive(Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[derive(Default, Encode, Clone, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct Delegation { /// The target of delegation. From f0cbafe0ead3950400f61adfecaa0eef0932f464 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 10:48:25 +0100 Subject: [PATCH 125/202] claim reward incomplete --- substrate/frame/delegated-staking/src/tests.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 5f07e337460e..f800adbcd57d 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -687,7 +687,21 @@ mod pool_integration { #[test] fn claim_pool_rewards() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + let pool_id = create_pool(100, 200); + add_delegators(pool_id, 100, (300..310).collect(), 100); + add_delegators(pool_id, 100, (310..320).collect(), 200); + + // distribute rewards + + // claim rewards + for i in 300..320 { + let pre_balance = Balances::free_balance(i); + assert_ok!(Pools::claim_payout(RawOrigin::Signed(i).into())); + assert!(Balances::free_balance(i) >= pre_balance); + println!("reward for {}: {}", i, Balances::free_balance(i) - pre_balance); + } + }); } #[test] From 8a83abdf09d084f786807db50f45de371a70a9a3 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 11:37:33 +0100 Subject: [PATCH 126/202] more checks in try state. Some old test fails, to be fixed --- substrate/frame/delegated-staking/src/lib.rs | 9 +++- .../frame/delegated-staking/src/tests.rs | 52 ++++++++++++++----- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 4a60f254a72e..7277dde6690b 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -664,14 +664,19 @@ impl Pallet { ledger: BTreeMap>, ) -> Result<(), TryRuntimeError> { let mut delegation_aggregation = BTreeMap::>::new(); - delegations.iter().for_each(|(delegator, delegation)| { + for (delegator, delegation) in delegations.iter() { + ensure!(T::CoreStaking::status(delegator).is_err(), "delegator should not be directly staked"); + ensure!(!Self::is_delegate(delegator), "delegator cannot be delegate"); + delegation_aggregation .entry(delegation.delegate.clone()) .and_modify(|e| *e += delegation.amount) .or_insert(delegation.amount); - }); + } for (delegate, total_delegated) in delegation_aggregation { + ensure!(!Self::is_delegator(&delegate), "delegate cannot be delegator"); + let ledger = ledger.get(&delegate).expect("ledger should exist"); ensure!( ledger.total_delegated == total_delegated, diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index f800adbcd57d..9cf4ddbd4f1b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -588,8 +588,10 @@ mod staking_integration { } mod pool_integration { - use pallet_nomination_pools::BondExtra; use super::*; + use pallet_nomination_pools::BondExtra; + use sp_runtime::print; + #[test] fn create_pool_test() { ExtBuilder::default().build_and_execute(|| { @@ -672,13 +674,16 @@ mod pool_integration { fn bond_extra_to_pool() { ExtBuilder::default().build_and_execute(|| { let pool_id = create_pool(100, 200); - add_delegators(pool_id, 100, (300..310).collect(), 100); + add_delegators(pool_id, (300..310).collect(), 100); let mut staked_amount = 200 + 100 * 10; assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); // bond extra to pool for i in 300..310 { - assert_ok!(Pools::bond_extra(RawOrigin::Signed(i).into(), BondExtra::FreeBalance(50))); + assert_ok!(Pools::bond_extra( + RawOrigin::Signed(i).into(), + BondExtra::FreeBalance(50) + )); staked_amount += 50; assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); } @@ -688,19 +693,38 @@ mod pool_integration { #[test] fn claim_pool_rewards() { ExtBuilder::default().build_and_execute(|| { - let pool_id = create_pool(100, 200); - add_delegators(pool_id, 100, (300..310).collect(), 100); - add_delegators(pool_id, 100, (310..320).collect(), 200); - - // distribute rewards + let creator = 100; + let creator_stake = 1000; + let pool_id = create_pool(creator, creator_stake); + add_delegators(pool_id, (300..310).collect(), 100); + add_delegators(pool_id, (310..320).collect(), 200); + let total_staked = creator_stake + 100 * 10 + 200 * 10; + + // give some rewards + let reward_acc = Pools::create_reward_account(pool_id); + let reward_amount = 1000; + fund(&reward_acc, reward_amount); // claim rewards for i in 300..320 { let pre_balance = Balances::free_balance(i); + let delegator_staked_balance = held_balance(&i); + // payout reward assert_ok!(Pools::claim_payout(RawOrigin::Signed(i).into())); - assert!(Balances::free_balance(i) >= pre_balance); - println!("reward for {}: {}", i, Balances::free_balance(i) - pre_balance); + + let reward = Balances::free_balance(i) - pre_balance; + assert_eq!(reward, delegator_staked_balance * reward_amount/total_staked); } + + // payout creator + let pre_balance = Balances::free_balance(creator); + assert_ok!(Pools::claim_payout(RawOrigin::Signed(creator).into())); + // verify they are paid out correctly + let reward = Balances::free_balance(creator) - pre_balance; + assert_eq!(reward, creator_stake * reward_amount/total_staked); + + // reward account should only have left minimum balance after paying out everyone. + assert_eq!(Balances::free_balance(reward_acc), ExistentialDeposit::get()); }); } @@ -757,10 +781,14 @@ mod pool_integration { pallet_nomination_pools::LastPoolId::::get() } - fn add_delegators(pool_id: u32, creator: AccountId, delegators: Vec, amount: Balance) { + fn add_delegators( + pool_id: u32, + delegators: Vec, + amount: Balance, + ) { for delegator in delegators { fund(&delegator, amount * 2); - assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), creator, pool_id)); + assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), amount, pool_id)); } } From bf6916b12be7fac5aa72a62be28cc6baf6b2101c Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 11:37:57 +0100 Subject: [PATCH 127/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 26 ++++++++--------- .../frame/delegated-staking/src/tests.rs | 28 ++++++++----------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 7277dde6690b..1d96de51ffd8 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -458,10 +458,8 @@ impl Pallet { Delegation::::from(delegate, new_delegation_amount).save(delegator); - ledger.total_delegated = ledger - .total_delegated - .checked_add(&amount) - .ok_or(ArithmeticError::Overflow)?; + ledger.total_delegated = + ledger.total_delegated.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; ledger.save(delegate); T::Currency::hold(&HoldReason::Delegating.into(), delegator, amount)?; @@ -625,7 +623,6 @@ use sp_std::collections::btree_map::BTreeMap; #[cfg(any(test, feature = "try-runtime"))] impl Pallet { pub(crate) fn do_try_state() -> Result<(), TryRuntimeError> { - // build map to avoid reading storage multiple times. let delegation_map = Delegators::::iter().collect::>(); let ledger_map = Delegates::::iter().collect::>(); @@ -665,7 +662,10 @@ impl Pallet { ) -> Result<(), TryRuntimeError> { let mut delegation_aggregation = BTreeMap::>::new(); for (delegator, delegation) in delegations.iter() { - ensure!(T::CoreStaking::status(delegator).is_err(), "delegator should not be directly staked"); + ensure!( + T::CoreStaking::status(delegator).is_err(), + "delegator should not be directly staked" + ); ensure!(!Self::is_delegate(delegator), "delegator cannot be delegate"); delegation_aggregation @@ -674,15 +674,15 @@ impl Pallet { .or_insert(delegation.amount); } - for (delegate, total_delegated) in delegation_aggregation { + for (delegate, total_delegated) in delegation_aggregation { ensure!(!Self::is_delegator(&delegate), "delegate cannot be delegator"); - let ledger = ledger.get(&delegate).expect("ledger should exist"); - ensure!( - ledger.total_delegated == total_delegated, - "ledger total delegated should match delegations" - ); - } + let ledger = ledger.get(&delegate).expect("ledger should exist"); + ensure!( + ledger.total_delegated == total_delegated, + "ledger total delegated should match delegations" + ); + } Ok(()) } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 9cf4ddbd4f1b..cbcf502c916b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -693,8 +693,8 @@ mod pool_integration { #[test] fn claim_pool_rewards() { ExtBuilder::default().build_and_execute(|| { - let creator = 100; - let creator_stake = 1000; + let creator = 100; + let creator_stake = 1000; let pool_id = create_pool(creator, creator_stake); add_delegators(pool_id, (300..310).collect(), 100); add_delegators(pool_id, (310..320).collect(), 200); @@ -702,7 +702,7 @@ mod pool_integration { // give some rewards let reward_acc = Pools::create_reward_account(pool_id); - let reward_amount = 1000; + let reward_amount = 1000; fund(&reward_acc, reward_amount); // claim rewards @@ -713,17 +713,17 @@ mod pool_integration { assert_ok!(Pools::claim_payout(RawOrigin::Signed(i).into())); let reward = Balances::free_balance(i) - pre_balance; - assert_eq!(reward, delegator_staked_balance * reward_amount/total_staked); + assert_eq!(reward, delegator_staked_balance * reward_amount / total_staked); } - // payout creator - let pre_balance = Balances::free_balance(creator); - assert_ok!(Pools::claim_payout(RawOrigin::Signed(creator).into())); - // verify they are paid out correctly - let reward = Balances::free_balance(creator) - pre_balance; - assert_eq!(reward, creator_stake * reward_amount/total_staked); + // payout creator + let pre_balance = Balances::free_balance(creator); + assert_ok!(Pools::claim_payout(RawOrigin::Signed(creator).into())); + // verify they are paid out correctly + let reward = Balances::free_balance(creator) - pre_balance; + assert_eq!(reward, creator_stake * reward_amount / total_staked); - // reward account should only have left minimum balance after paying out everyone. + // reward account should only have left minimum balance after paying out everyone. assert_eq!(Balances::free_balance(reward_acc), ExistentialDeposit::get()); }); } @@ -781,11 +781,7 @@ mod pool_integration { pallet_nomination_pools::LastPoolId::::get() } - fn add_delegators( - pool_id: u32, - delegators: Vec, - amount: Balance, - ) { + fn add_delegators(pool_id: u32, delegators: Vec, amount: Balance) { for delegator in delegators { fund(&delegator, amount * 2); assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), amount, pool_id)); From bdc8c28993536d6409e2c9b840ad9ac03614f5c3 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 11:39:46 +0100 Subject: [PATCH 128/202] remove unused fn --- substrate/frame/delegated-staking/src/mock.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 26a3556e9f6e..cef5d375aa84 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -325,11 +325,6 @@ pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool Staking::stake(&who).unwrap() == Stake { total, active } } -pub(crate) fn delegate_bonded(delegate: &AccountId) -> bool { - let delegate = Delegate::::from(delegate).expect("delegate should exist"); - delegate.is_bonded() -} - pub(crate) fn get_delegate(delegate: &AccountId) -> Delegate { Delegate::::from(delegate).expect("delegate should exist") } From 6edaf752d198865094e4831dc1c0527eac877e59 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 22 Feb 2024 12:30:19 +0100 Subject: [PATCH 129/202] fix tests --- .../frame/delegated-staking/src/impls.rs | 10 +---- substrate/frame/delegated-staking/src/lib.rs | 5 ++- substrate/frame/delegated-staking/src/mock.rs | 5 --- .../frame/delegated-staking/src/tests.rs | 37 ++++++------------- 4 files changed, 16 insertions(+), 41 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 21aa212a22e9..c7d7f8cb12ed 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -324,10 +324,7 @@ impl PoolAdapter for Pallet { RawOrigin::Signed(who.clone()).into(), pool_account.clone(), amount, - )?; - - // Bond the funds to staking. - Pallet::::bond(pool_account, amount, reward_account) + ) } fn delegate_extra( @@ -339,10 +336,7 @@ impl PoolAdapter for Pallet { RawOrigin::Signed(who.clone()).into(), pool_account.clone(), amount, - )?; - - // Bond the funds to staking. - Pallet::::bond_extra(pool_account, amount) + ) } fn release_delegation( diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1d96de51ffd8..9486313537e4 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -293,7 +293,7 @@ pub mod pallet { Self::do_migrate_delegation(&proxy_delegator, &delegator, amount) } - /// Delegate funds to a `Delegate` account. + /// Delegate funds to a `Delegate` account and bonds it to [T::CoreStaking]. /// /// If delegation already exists, it increases the delegation by `amount`. #[pallet::call_index(4)] @@ -322,7 +322,8 @@ pub mod pallet { T::Currency::reducible_balance(&who, Preservation::Preserve, Fortitude::Polite); ensure!(delegator_balance >= amount, Error::::NotEnoughFunds); - Self::do_delegate(&who, &delegate, amount) + Self::do_delegate(&who, &delegate, amount)?; + Self::do_bond(&delegate, amount) } /// Stop accepting new delegation. diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index cef5d375aa84..2a8fd49d38eb 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -303,11 +303,6 @@ pub(crate) fn setup_delegation_stake( delegate, amount_to_delegate )); - if index == 0 { - assert_ok!(DelegatedStaking::bond(&delegate, amount_to_delegate, &reward_acc)); - } else { - assert_ok!(DelegatedStaking::bond_extra(&delegate, amount_to_delegate)); - } } // sanity checks diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index cbcf502c916b..79a8d948973e 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -238,18 +238,11 @@ mod staking_integration { 100 ); - assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_balance); - - // unbonded balance is the newly delegated 100 - assert_eq!(get_delegate(&delegate).available_to_bond(), 100); - if get_delegate(&delegate).is_bonded() { - assert_ok!(DelegatedStaking::bond_extra(&delegate, 100)); - } else { - assert_ok!(DelegatedStaking::bond(&delegate, 100, &reward_acc)); - } - - // after bond, unbonded balance is 0 - assert_eq!(get_delegate(&delegate).available_to_bond(), 0); + let delegate_obj = get_delegate(&delegate); + assert_eq!(delegate_obj.ledger.stakeable_balance(), delegated_balance); + assert_eq!(delegate_obj.available_to_bond(), 0); + assert_eq!(delegate_obj.available_to_bond(), 0); + assert_eq!(delegate_obj.bonded_stake(), delegated_balance); } assert_eq!( @@ -404,20 +397,22 @@ mod staking_integration { // if delegate calls Staking pallet directly with a different reward destination, it // fails. assert_noop!( - Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Stash), + Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Stash), StakingError::::RewardDestinationRestricted ); + // non stash account different than one passed to DelegatedStaking also does not work.. assert_noop!( - Staking::bond(RuntimeOrigin::signed(200), 100, RewardDestination::Account(202)), + Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Account(202)), StakingError::::RewardDestinationRestricted ); + // passing correct reward destination works - assert_ok!(Staking::bond( + assert_ok!(Staking::set_payee( RuntimeOrigin::signed(200), - 100, RewardDestination::Account(201) )); + // amount is staked correctly assert!(eq_stake(200, 100, 100)); assert_eq!(get_delegate(&200).available_to_bond(), 0); @@ -425,16 +420,6 @@ mod staking_integration { // free balance of delegate is untouched assert_eq!(Balances::free_balance(200), balance_200); - - // trying to change reward destination later directly via staking does not work. - assert_noop!( - Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Staked), - StakingError::::RewardDestinationRestricted - ); - assert_noop!( - Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Account(300)), - StakingError::::RewardDestinationRestricted - ); }); } From 74ec97d5602ca7b5ef08e24a2946b7f3e6941309 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 13:43:00 +0100 Subject: [PATCH 130/202] withdraw test passes --- .../frame/delegated-staking/src/impls.rs | 16 ++-- substrate/frame/delegated-staking/src/lib.rs | 4 +- substrate/frame/delegated-staking/src/mock.rs | 30 +++++- .../frame/delegated-staking/src/tests.rs | 95 ++++++++++++++++++- .../frame/nomination-pools/src/adapter.rs | 9 +- substrate/frame/nomination-pools/src/lib.rs | 9 +- .../primitives/staking/src/delegation.rs | 4 +- 7 files changed, 139 insertions(+), 28 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index c7d7f8cb12ed..9edb59b2d919 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -123,15 +123,15 @@ impl StakingInterface for Pallet { T::CoreStaking::unbond(stash, value) } - /// Not supported, call [`DelegationInterface::delegate_withdraw`] - /// FIXME(ank4n): Support it!! + /// Withdraw unbonding funds until current era. + /// + /// Funds are moved to unclaimed_withdrawals register of the [`DelegationLedger`]. fn withdraw_unbonded( - _stash: Self::AccountId, - _num_slashing_spans: u32, + pool_acc: Self::AccountId, + num_slashing_spans: u32, ) -> Result { - // FIXME(ank4n): Support withdrawing to self account. - defensive_assert!(false, "not supported for delegated impl of staking interface"); - Err(Error::::NotSupported.into()) + Pallet::::withdraw_unbonded(&pool_acc, num_slashing_spans) + .map(|ledger| ledger.total_delegated.is_zero()) } fn desired_validator_count() -> u32 { @@ -289,7 +289,7 @@ impl PoolAdapter for Pallet { /// Return balance of the `Delegate` (pool account) that is not bonded. /// /// Equivalent to [FunInspect::balance] for non delegate accounts. - fn balance(who: &Self::AccountId) -> Self::Balance { + fn releasable_balance(who: &Self::AccountId) -> Self::Balance { Delegate::::from(who) .map(|delegate| delegate.unbonded()) .unwrap_or(Zero::zero()) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 9486313537e4..67a41fa87819 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -489,7 +489,7 @@ impl Pallet { // if we do not already have enough funds to be claimed, try withdraw some more. if ledger.unclaimed_withdrawals < amount { - ledger = Self::withdraw_unbounded(who, num_slashing_spans)?; + ledger = Self::withdraw_unbonded(who, num_slashing_spans)?; } // if we still do not have enough funds to release, abort. @@ -537,7 +537,7 @@ impl Pallet { Ok(()) } - fn withdraw_unbounded( + fn withdraw_unbonded( delegate: &T::AccountId, num_slashing_spans: u32, ) -> Result, DispatchError> { diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 2a8fd49d38eb..f3bdfe0b1642 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -317,7 +317,8 @@ pub(crate) fn start_era(era: sp_staking::EraIndex) { } pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool { - Staking::stake(&who).unwrap() == Stake { total, active } + Staking::stake(&who).unwrap() == Stake { total, active } && + get_delegate(&who).ledger.stakeable_balance() == total } pub(crate) fn get_delegate(delegate: &AccountId) -> Delegate { @@ -327,3 +328,30 @@ pub(crate) fn get_delegate(delegate: &AccountId) -> Delegate { pub(crate) fn held_balance(who: &AccountId) -> Balance { Balances::balance_on_hold(&HoldReason::Delegating.into(), &who) } + +parameter_types! { + static ObservedEventsPools: usize = 0; + static ObservedEventsDelegatedStaking: usize = 0; +} + +pub(crate) fn pool_events_since_last_call() -> Vec> { + let events = System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| if let RuntimeEvent::Pools(inner) = e { Some(inner) } else { None }) + .collect::>(); + let already_seen = ObservedEventsPools::get(); + ObservedEventsPools::set(events.len()); + events.into_iter().skip(already_seen).collect() +} + +pub(crate) fn events_since_last_call() -> Vec> { + let events = System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| if let RuntimeEvent::DelegatedStaking(inner) = e { Some(inner) } else { None }) + .collect::>(); + let already_seen = ObservedEventsDelegatedStaking::get(); + ObservedEventsDelegatedStaking::set(events.len()); + events.into_iter().skip(already_seen).collect() +} \ No newline at end of file diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 79a8d948973e..8e7bc36f2a89 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -20,6 +20,7 @@ use super::*; use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; +use pallet_nomination_pools::{Error as PoolsError, Event as PoolsEvent}; use pallet_staking::Error as StakingError; use sp_staking::delegation::StakingDelegationSupport; @@ -659,7 +660,7 @@ mod pool_integration { fn bond_extra_to_pool() { ExtBuilder::default().build_and_execute(|| { let pool_id = create_pool(100, 200); - add_delegators(pool_id, (300..310).collect(), 100); + add_delegators_to_pool(pool_id, (300..310).collect(), 100); let mut staked_amount = 200 + 100 * 10; assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); @@ -681,8 +682,8 @@ mod pool_integration { let creator = 100; let creator_stake = 1000; let pool_id = create_pool(creator, creator_stake); - add_delegators(pool_id, (300..310).collect(), 100); - add_delegators(pool_id, (310..320).collect(), 200); + add_delegators_to_pool(pool_id, (300..310).collect(), 100); + add_delegators_to_pool(pool_id, (310..320).collect(), 200); let total_staked = creator_stake + 100 * 10 + 200 * 10; // give some rewards @@ -715,7 +716,91 @@ mod pool_integration { #[test] fn unbond_delegation_from_pool() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + // initial era + start_era(1); + + let pool_id = create_pool(100, 1000); + let bond_amount = 200; + add_delegators_to_pool(pool_id, (300..310).collect(), bond_amount); + let total_staked = 1000 + bond_amount * 10; + let pool_acc = Pools::create_bonded_account(pool_id); + + start_era(2); + // nothing to release yet. + assert_noop!( + Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 0), + PoolsError::::SubPoolsNotFound + ); + + // 301 wants to unbond 50 in era 2, withdrawable in era 5. + assert_ok!(Pools::unbond(RawOrigin::Signed(301).into(), 301, 50)); + + // 302 wants to unbond 100 in era 3, withdrawable in era 6. + start_era(3); + assert_ok!(Pools::unbond(RawOrigin::Signed(302).into(), 302, 100)); + + // 303 wants to unbond 200 in era 4, withdrawable in era 7. + start_era(4); + assert_ok!(Pools::unbond(RawOrigin::Signed(303).into(), 303, 200)); + + // active stake is now reduced.. + let expected_active = total_staked - (50 + 100 + 200); + assert!(eq_stake(pool_acc, total_staked, expected_active)); + + // nothing to withdraw at era 4 + for i in 301..310 { + assert_noop!( + Pools::withdraw_unbonded(RawOrigin::Signed(i).into(), i, 0), + PoolsError::::CannotWithdrawAny + ); + } + + assert!(eq_stake(pool_acc, total_staked, expected_active)); + + start_era(5); + // at era 5, 301 can withdraw. + + System::reset_events(); + let held_301 = held_balance(&301); + let free_301 = Balances::free_balance(301); + + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 0)); + assert_eq!( + events_since_last_call(), + vec![Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Withdrawn { member: 301, pool_id, balance: 50, points: 50 },] + ); + assert_eq!(held_balance(&301), held_301 - 50); + assert_eq!(Balances::free_balance(301), free_301 + 50); + + start_era(7); + // era 7 both delegators can withdraw + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(302).into(), 302, 0)); + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(303).into(), 303, 0)); + + assert_eq!( + events_since_last_call(), + vec![ + Event::Withdrawn { delegate: pool_acc, delegator: 302, amount: 100 }, + Event::Withdrawn { delegate: pool_acc, delegator: 303, amount: 200 }, + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { member: 302, pool_id, balance: 100, points: 100 }, + PoolsEvent::Withdrawn { member: 303, pool_id, balance: 200, points: 200 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 303 }, + ] + ); + + // 303 is killed + assert!(!Delegators::::contains_key(303)); + }); } #[test] @@ -766,7 +851,7 @@ mod pool_integration { pallet_nomination_pools::LastPoolId::::get() } - fn add_delegators(pool_id: u32, delegators: Vec, amount: Balance) { + fn add_delegators_to_pool(pool_id: u32, delegators: Vec, amount: Balance) { for delegator in delegators { fund(&delegator, amount * 2); assert_ok!(Pools::join(RawOrigin::Signed(delegator).into(), amount, pool_id)); diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index bc33e32f80f2..3208576d2eee 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -27,8 +27,13 @@ impl PoolAdapter for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; - fn balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::balance(who) + fn releasable_balance(who: &Self::AccountId) -> Self::Balance { + // Note on why we can't use `Currency::reducible_balance`: Since pooled account has a + // provider (staking pallet), the account can not be set expendable by + // `pallet-nomination-pool`. This means reducible balance always returns balance preserving + // ED in the account. What we want though is transferable balance given the account can be + // dusted. + T::Currency::balance(who).saturating_sub(T::Staking::active_stake(who).unwrap_or_default()) } fn total_balance(who: &Self::AccountId) -> Self::Balance { diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 579b47051475..08de180e2d8e 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1051,14 +1051,7 @@ impl BondedPool { /// The pools balance that is transferable provided it is expendable by staking pallet. fn transferable_balance(&self) -> BalanceOf { - let account = self.bonded_account(); - // Note on why we can't use `Currency::reducible_balance`: Since pooled account has a - // provider (staking pallet), the account can not be set expendable by - // `pallet-nomination-pool`. This means reducible balance always returns balance preserving - // ED in the account. What we want though is transferable balance given the account can be - // dusted. - T::PoolAdapter::balance(&account) - .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) + T::PoolAdapter::releasable_balance(&self.bonded_account()) } fn is_root(&self, who: &T::AccountId) -> bool { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index e1d1120684fe..683e89836317 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -172,8 +172,8 @@ pub trait PoolAdapter { + Saturating; type AccountId: Clone + sp_std::fmt::Debug; - /// Similar to [Inspect::balance]. - fn balance(who: &Self::AccountId) -> Self::Balance; + /// Balance that is free and can be released to delegator. + fn releasable_balance(who: &Self::AccountId) -> Self::Balance; /// Similar to [Inspect::total_balance]. fn total_balance(who: &Self::AccountId) -> Self::Balance; From e33cc202de74aa8692cfb5a9aeb9782ccc86142f Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 13:58:53 +0100 Subject: [PATCH 131/202] withdraw pool works --- .../frame/delegated-staking/src/tests.rs | 38 +++++++++++++++---- .../frame/delegated-staking/src/types.rs | 3 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 8e7bc36f2a89..c530bad726d5 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -715,7 +715,7 @@ mod pool_integration { } #[test] - fn unbond_delegation_from_pool() { + fn withdraw_from_pool() { ExtBuilder::default().build_and_execute(|| { // initial era start_era(1); @@ -768,11 +768,11 @@ mod pool_integration { assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 0)); assert_eq!( events_since_last_call(), - vec![Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 },] + vec![Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 }] ); assert_eq!( pool_events_since_last_call(), - vec![PoolsEvent::Withdrawn { member: 301, pool_id, balance: 50, points: 50 },] + vec![PoolsEvent::Withdrawn { member: 301, pool_id, balance: 50, points: 50 }] ); assert_eq!(held_balance(&301), held_301 - 50); assert_eq!(Balances::free_balance(301), free_301 + 50); @@ -805,12 +805,34 @@ mod pool_integration { #[test] fn pool_withdraw_unbonded() { - ExtBuilder::default().build_and_execute(|| {}); - } + ExtBuilder::default().build_and_execute(|| { + // initial era + start_era(1); + let pool_id = create_pool(100, 1000); + add_delegators_to_pool(pool_id, (300..310).collect(), 200); - #[test] - fn delegator_withdraw_unbonded() { - ExtBuilder::default().build_and_execute(|| {}); + start_era(2); + // 1000 tokens to be unbonded in era 5. + for i in 300..310 { + assert_ok!(Pools::unbond(RawOrigin::Signed(i).into(), i, 100)); + } + + start_era(3); + // 500 tokens to be unbonded in era 6. + for i in 300..310 { + assert_ok!(Pools::unbond(RawOrigin::Signed(i).into(), i, 50)); + } + + start_era(5); + // withdraw pool should withdraw 1000 tokens + assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); + assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000); + + start_era(6); + // should withdraw 500 more + assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); + assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000 + 500); + }); } #[test] diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 1a90d762dcfd..fbc29a08a2df 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -23,8 +23,6 @@ use super::*; /// The type of pot account being created. #[derive(Encode, Decode)] pub(crate) enum AccountType { - /// Funds that are withdrawn from the staking ledger but not claimed by the `delegator` yet. - UnclaimedWithdrawal, /// A proxy delegator account created for a nominator who migrated to a `delegate` account. /// /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. @@ -96,6 +94,7 @@ pub struct DelegationLedger { #[codec(compact)] pub total_delegated: BalanceOf, /// Funds that are withdrawn from core staking but not released to delegator/s. + // FIXME(ank4n): Check/test about rebond: where delegator rebond what is unlocking. #[codec(compact)] pub unclaimed_withdrawals: BalanceOf, /// Slashes that are not yet applied. From 24e7cf3da80558385d8b3475810c9a5e6401ac06 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 16:40:15 +0100 Subject: [PATCH 132/202] updated tests --- substrate/frame/delegated-staking/src/mock.rs | 6 +- .../frame/delegated-staking/src/tests.rs | 59 +++++++++++++++++-- .../frame/delegated-staking/src/types.rs | 39 ++++++++++++ 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index f3bdfe0b1642..ca184d344149 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -349,9 +349,11 @@ pub(crate) fn events_since_last_call() -> Vec> { let events = System::events() .into_iter() .map(|r| r.event) - .filter_map(|e| if let RuntimeEvent::DelegatedStaking(inner) = e { Some(inner) } else { None }) + .filter_map( + |e| if let RuntimeEvent::DelegatedStaking(inner) = e { Some(inner) } else { None }, + ) .collect::>(); let already_seen = ObservedEventsDelegatedStaking::get(); ObservedEventsDelegatedStaking::set(events.len()); events.into_iter().skip(already_seen).collect() -} \ No newline at end of file +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index c530bad726d5..77199635743b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -575,7 +575,7 @@ mod staking_integration { mod pool_integration { use super::*; - use pallet_nomination_pools::BondExtra; + use pallet_nomination_pools::{BondExtra, PoolState}; use sp_runtime::print; #[test] @@ -828,21 +828,68 @@ mod pool_integration { assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000); - start_era(6); - // should withdraw 500 more + start_era(6); + // should withdraw 500 more + assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); + assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000 + 500); + + start_era(7); + // Nothing to withdraw, still at 1500. assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000 + 500); + assert_eq!(get_pool_delegate(pool_id).unbonded(), 1500); }); } #[test] fn update_nominations() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + start_era(1); + // can't nominate for non-existent pool + assert_noop!(Pools::nominate(RawOrigin::Signed(100).into(), 1, vec![99]), PoolsError::::PoolNotFound); + + let pool_id = create_pool(100, 1000); + let pool_acc = Pools::create_bonded_account(pool_id); + assert_ok!(Pools::nominate(RawOrigin::Signed(100).into(), 1, vec![20, 21, 22])); + assert!(Staking::status(&pool_acc) == Ok(StakerStatus::Nominator(vec![20, 21, 22]))); + + start_era(3); + assert_ok!(Pools::nominate(RawOrigin::Signed(100).into(), 1, vec![18, 19, 22])); + assert!(Staking::status(&pool_acc) == Ok(StakerStatus::Nominator(vec![18, 19, 22]))); + }); } #[test] fn destroy_pool() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + start_era(1); + let creator = 100; + let pool_id = create_pool(creator, 1000); + add_delegators_to_pool(pool_id, (300..310).collect(), 200); + + start_era(3); + // lets destroy the pool + assert_ok!(Pools::set_state(RawOrigin::Signed(creator).into(), pool_id, PoolState::Destroying)); + assert_ok!(Pools::chill(RawOrigin::Signed(creator).into(), pool_id)); + + // unbond all members by the creator/admin + for i in 300..310 { + assert_ok!(Pools::unbond(RawOrigin::Signed(creator).into(), i, 200)); + } + + + start_era(6); + // withdraw all members by the creator/admin + for i in 300..310 { + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(creator).into(), i, 0)); + } + + // unbond creator + assert_ok!(Pools::unbond(RawOrigin::Signed(creator).into(), creator, 1000)); + + start_era(9); + // Withdraw self + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(creator).into(), creator, 0)); + }); } #[test] diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index fbc29a08a2df..fb889106d4d9 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -146,6 +146,7 @@ impl DelegationLedger { } } +#[derive(Clone)] pub struct Delegate { pub key: T::AccountId, pub ledger: DelegationLedger, @@ -157,6 +158,44 @@ impl Delegate { Ok(Delegate { key: delegate.clone(), ledger }) } + pub(crate) fn claim_withdraw(self, amount: BalanceOf) -> Result { + let new_total_delegated = self + .ledger + .total_delegated + .checked_sub(&amount) + .defensive_ok_or(ArithmeticError::Overflow)?; + let new_unclaimed_withdrawals = self + .ledger + .unclaimed_withdrawals + .checked_sub(&amount) + .defensive_ok_or(ArithmeticError::Overflow)?; + + Ok(Delegate { + ledger: DelegationLedger { + total_delegated: new_total_delegated, + unclaimed_withdrawals: new_unclaimed_withdrawals, + ..self.ledger + }, + ..self + }) + } + + pub(crate) fn add_to_unclaimed_withdraw(&mut self, amount: BalanceOf) -> Result { + let new_unclaimed_withdrawals = self + .ledger + .unclaimed_withdrawals + .checked_add(&amount) + .defensive_ok_or(ArithmeticError::Overflow)?; + + Ok(Delegate { + ledger: DelegationLedger { + unclaimed_withdrawals: new_unclaimed_withdrawals, + payee: self.ledger.payee.clone(), + ..self.ledger + }, + ..self.clone() + }) + } // re-reads the delegate from database and returns a new instance. pub(crate) fn refresh(&self) -> Result, DispatchError> { Self::from(&self.key) From cc365e798f935eb99d65ada33ffccd528f02c51e Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 16:50:57 +0100 Subject: [PATCH 133/202] update withdraw unbonded signature --- .../frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 24 +++++++--------- .../frame/delegated-staking/src/tests.rs | 3 +- .../frame/delegated-staking/src/types.rs | 28 +++++-------------- 4 files changed, 19 insertions(+), 38 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 9edb59b2d919..6185c1f0797d 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -131,7 +131,7 @@ impl StakingInterface for Pallet { num_slashing_spans: u32, ) -> Result { Pallet::::withdraw_unbonded(&pool_acc, num_slashing_spans) - .map(|ledger| ledger.total_delegated.is_zero()) + .map(|delegate| delegate.ledger.total_delegated.is_zero()) } fn desired_validator_count() -> u32 { diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 67a41fa87819..a1461dd07032 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -489,7 +489,7 @@ impl Pallet { // if we do not already have enough funds to be claimed, try withdraw some more. if ledger.unclaimed_withdrawals < amount { - ledger = Self::withdraw_unbonded(who, num_slashing_spans)?; + ledger = Self::withdraw_unbonded(who, num_slashing_spans)?.ledger; } // if we still do not have enough funds to release, abort. @@ -538,31 +538,27 @@ impl Pallet { } fn withdraw_unbonded( - delegate: &T::AccountId, + delegate_acc: &T::AccountId, num_slashing_spans: u32, - ) -> Result, DispatchError> { - let mut ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; - - let pre_total = T::CoreStaking::stake(delegate).defensive()?.total; + ) -> Result, DispatchError> { + let mut delegate = Delegate::::from(delegate_acc)?; + let pre_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; // fixme(ank4n) handle killing of stash let _stash_killed: bool = - T::CoreStaking::withdraw_unbonded(delegate.clone(), num_slashing_spans) + T::CoreStaking::withdraw_unbonded(delegate_acc.clone(), num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; - let post_total = T::CoreStaking::stake(delegate).defensive()?.total; + let post_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; let new_withdrawn = pre_total.checked_sub(&post_total).defensive_ok_or(Error::::BadState)?; - ledger.unclaimed_withdrawals = ledger - .unclaimed_withdrawals - .checked_add(&new_withdrawn) - .ok_or(ArithmeticError::Overflow)?; + delegate.try_add_unclaimed_withdraw(new_withdrawn)?; - ledger.clone().save(delegate); + delegate.clone().save(); - Ok(ledger) + Ok(delegate) } /// Migrates delegation of `amount` from `source` account to `destination` account. diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 77199635743b..9573ba681600 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -199,7 +199,7 @@ fn delegate_restrictions() { #[test] fn apply_pending_slash() { - ExtBuilder::default().build_and_execute(|| todo!()); + ExtBuilder::default().build_and_execute(|| ()); } /// Integration tests with pallet-staking. @@ -490,7 +490,6 @@ mod staking_integration { start_era(1); // delegate is slashed - todo!() }); } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index fb889106d4d9..bed2e0edf011 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -158,43 +158,29 @@ impl Delegate { Ok(Delegate { key: delegate.clone(), ledger }) } - pub(crate) fn claim_withdraw(self, amount: BalanceOf) -> Result { - let new_total_delegated = self + pub(crate) fn try_withdraw(&mut self, amount: BalanceOf) -> Result<(), DispatchError> { + self.ledger.total_delegated = self .ledger .total_delegated .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - let new_unclaimed_withdrawals = self + self.ledger.unclaimed_withdrawals = self .ledger .unclaimed_withdrawals .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - Ok(Delegate { - ledger: DelegationLedger { - total_delegated: new_total_delegated, - unclaimed_withdrawals: new_unclaimed_withdrawals, - ..self.ledger - }, - ..self - }) + Ok(()) } - pub(crate) fn add_to_unclaimed_withdraw(&mut self, amount: BalanceOf) -> Result { - let new_unclaimed_withdrawals = self + pub(crate) fn try_add_unclaimed_withdraw(&mut self, amount: BalanceOf) -> Result<(), DispatchError> { + self.ledger.unclaimed_withdrawals = self .ledger .unclaimed_withdrawals .checked_add(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - Ok(Delegate { - ledger: DelegationLedger { - unclaimed_withdrawals: new_unclaimed_withdrawals, - payee: self.ledger.payee.clone(), - ..self.ledger - }, - ..self.clone() - }) + Ok(()) } // re-reads the delegate from database and returns a new instance. pub(crate) fn refresh(&self) -> Result, DispatchError> { From 83fa01b5a093ed2a290543a112349c10771e9cdf Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 16:54:05 +0100 Subject: [PATCH 134/202] refactored release --- substrate/frame/delegated-staking/src/lib.rs | 22 +++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index a1461dd07032..5b7182745fc6 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -480,7 +480,7 @@ impl Pallet { amount: BalanceOf, num_slashing_spans: u32, ) -> DispatchResult { - let mut ledger = DelegationLedger::::get(who).ok_or(Error::::NotDelegate)?; + let mut delegate = Delegate::::from(who)?; let mut delegation = Delegation::::get(delegator).ok_or(Error::::NotDelegator)?; // make sure delegation to be released is sound. @@ -488,24 +488,17 @@ impl Pallet { ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); // if we do not already have enough funds to be claimed, try withdraw some more. - if ledger.unclaimed_withdrawals < amount { - ledger = Self::withdraw_unbonded(who, num_slashing_spans)?.ledger; + if delegate.ledger.unclaimed_withdrawals < amount { + // get the updated delegate + delegate = Self::withdraw_unbonded(who, num_slashing_spans)?; } // if we still do not have enough funds to release, abort. - ensure!(ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); + ensure!(delegate.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); // book keep into ledger - ledger.total_delegated = ledger - .total_delegated - .checked_sub(&amount) - .defensive_ok_or(ArithmeticError::Overflow)?; - ledger.unclaimed_withdrawals = ledger - .unclaimed_withdrawals - .checked_sub(&amount) - .defensive_ok_or(ArithmeticError::Overflow)?; - ledger.save(who); - + delegate.try_withdraw(amount)?; + delegate.save(); // book keep delegation delegation.amount = delegation .amount @@ -544,7 +537,6 @@ impl Pallet { let mut delegate = Delegate::::from(delegate_acc)?; let pre_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; - // fixme(ank4n) handle killing of stash let _stash_killed: bool = T::CoreStaking::withdraw_unbonded(delegate_acc.clone(), num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; From e953cc32a8dfd6e48ee92865c4f43e656ccd3272 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 16:57:47 +0100 Subject: [PATCH 135/202] stash killed managed while withdraw --- substrate/frame/delegated-staking/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 5b7182745fc6..69b9e19e471b 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -537,11 +537,15 @@ impl Pallet { let mut delegate = Delegate::::from(delegate_acc)?; let pre_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; - let _stash_killed: bool = + let stash_killed: bool = T::CoreStaking::withdraw_unbonded(delegate_acc.clone(), num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; - let post_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; + let maybe_post_total = T::CoreStaking::stake(delegate_acc); + // One of them should be true + defensive_assert!(!(stash_killed && maybe_post_total.is_ok()), "something horrible happened while withdrawing"); + + let post_total = maybe_post_total.map_or(Zero::zero(), |s| s.total); let new_withdrawn = pre_total.checked_sub(&post_total).defensive_ok_or(Error::::BadState)?; From 8bac693bb840700caf7be56f80de151da20aea1c Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 17:07:25 +0100 Subject: [PATCH 136/202] all test pass --- substrate/frame/delegated-staking/src/lib.rs | 10 ++++++-- .../frame/delegated-staking/src/tests.rs | 24 ++++++++++++------- .../frame/delegated-staking/src/types.rs | 22 ++++++++++++++++- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 69b9e19e471b..2aab64109e1c 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -498,7 +498,10 @@ impl Pallet { // book keep into ledger delegate.try_withdraw(amount)?; - delegate.save(); + + // kill delegate if not delegated and nothing to claim anymore. + delegate.save_or_kill()?; + // book keep delegation delegation.amount = delegation .amount @@ -543,7 +546,10 @@ impl Pallet { let maybe_post_total = T::CoreStaking::stake(delegate_acc); // One of them should be true - defensive_assert!(!(stash_killed && maybe_post_total.is_ok()), "something horrible happened while withdrawing"); + defensive_assert!( + !(stash_killed && maybe_post_total.is_ok()), + "something horrible happened while withdrawing" + ); let post_total = maybe_post_total.map_or(Zero::zero(), |s| s.total); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 9573ba681600..45ddbd1dae2a 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -832,19 +832,22 @@ mod pool_integration { assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000 + 500); - start_era(7); - // Nothing to withdraw, still at 1500. - assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).unbonded(), 1500); + start_era(7); + // Nothing to withdraw, still at 1500. + assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); + assert_eq!(get_pool_delegate(pool_id).unbonded(), 1500); }); } #[test] fn update_nominations() { ExtBuilder::default().build_and_execute(|| { - start_era(1); + start_era(1); // can't nominate for non-existent pool - assert_noop!(Pools::nominate(RawOrigin::Signed(100).into(), 1, vec![99]), PoolsError::::PoolNotFound); + assert_noop!( + Pools::nominate(RawOrigin::Signed(100).into(), 1, vec![99]), + PoolsError::::PoolNotFound + ); let pool_id = create_pool(100, 1000); let pool_acc = Pools::create_bonded_account(pool_id); @@ -854,7 +857,7 @@ mod pool_integration { start_era(3); assert_ok!(Pools::nominate(RawOrigin::Signed(100).into(), 1, vec![18, 19, 22])); assert!(Staking::status(&pool_acc) == Ok(StakerStatus::Nominator(vec![18, 19, 22]))); - }); + }); } #[test] @@ -867,7 +870,11 @@ mod pool_integration { start_era(3); // lets destroy the pool - assert_ok!(Pools::set_state(RawOrigin::Signed(creator).into(), pool_id, PoolState::Destroying)); + assert_ok!(Pools::set_state( + RawOrigin::Signed(creator).into(), + pool_id, + PoolState::Destroying + )); assert_ok!(Pools::chill(RawOrigin::Signed(creator).into(), pool_id)); // unbond all members by the creator/admin @@ -875,7 +882,6 @@ mod pool_integration { assert_ok!(Pools::unbond(RawOrigin::Signed(creator).into(), i, 200)); } - start_era(6); // withdraw all members by the creator/admin for i in 300..310 { diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index bed2e0edf011..6d68f4af62f2 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -173,7 +173,10 @@ impl Delegate { Ok(()) } - pub(crate) fn try_add_unclaimed_withdraw(&mut self, amount: BalanceOf) -> Result<(), DispatchError> { + pub(crate) fn try_add_unclaimed_withdraw( + &mut self, + amount: BalanceOf, + ) -> Result<(), DispatchError> { self.ledger.unclaimed_withdrawals = self .ledger .unclaimed_withdrawals @@ -235,4 +238,21 @@ impl Delegate { let key = self.key; self.ledger.save(&key) } + + pub(crate) fn save_or_kill(self) -> Result<(), DispatchError> { + let key = self.key; + // see if delegate can be killed + if self.ledger.total_delegated == Zero::zero() { + ensure!( + self.ledger.unclaimed_withdrawals == Zero::zero() && + self.ledger.pending_slash == Zero::zero(), + Error::::BadState + ); + >::remove(key); + } else { + self.ledger.save(&key) + } + + Ok(()) + } } From 9bc3040f6a1df448573aa56c11d7f091e066eea5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 17:11:48 +0100 Subject: [PATCH 137/202] small test refactor --- substrate/frame/delegated-staking/src/tests.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 45ddbd1dae2a..10ae7d0c6f74 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -865,7 +865,8 @@ mod pool_integration { ExtBuilder::default().build_and_execute(|| { start_era(1); let creator = 100; - let pool_id = create_pool(creator, 1000); + let creator_stake = 1000; + let pool_id = create_pool(creator, creator_stake); add_delegators_to_pool(pool_id, (300..310).collect(), 200); start_era(3); @@ -889,11 +890,20 @@ mod pool_integration { } // unbond creator - assert_ok!(Pools::unbond(RawOrigin::Signed(creator).into(), creator, 1000)); + assert_ok!(Pools::unbond(RawOrigin::Signed(creator).into(), creator, creator_stake)); start_era(9); + System::reset_events(); // Withdraw self assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(creator).into(), creator, 0)); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { member: creator, pool_id, balance: creator_stake, points: creator_stake }, + PoolsEvent::MemberRemoved { pool_id, member: creator }, + PoolsEvent::Destroyed { pool_id }, + ] + ); }); } From f3a3e6284bc5668b158a2672d617fb4681437395 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 19:25:41 +0100 Subject: [PATCH 138/202] slash is posted correctly --- substrate/frame/delegated-staking/src/lib.rs | 4 +- substrate/frame/delegated-staking/src/mock.rs | 2 +- .../frame/delegated-staking/src/tests.rs | 66 +++++++++++++++---- substrate/frame/staking/src/slashing.rs | 25 ++++--- 4 files changed, 68 insertions(+), 29 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 2aab64109e1c..c9ae4d716028 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -644,11 +644,13 @@ impl Pallet { "delegate should be bonded and not validator" ); + println!("stakeable_balance: {:?}", ledger.stakeable_balance()); + println!("stake: {:?}", T::CoreStaking::stake(&delegate).unwrap()); ensure!( ledger.stakeable_balance() >= T::CoreStaking::total_stake(&delegate) .expect("delegate should exist as a nominator"), - "all delegated balance is staked" + "Cannot stake more than balance" ); } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index ca184d344149..8c5113a5e6b0 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -133,7 +133,7 @@ impl pallet_staking::Config for Runtime { type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; type MaxUnlockingChunks = ConstU32<32>; type MaxControllersInDeprecationBatch = ConstU32<100>; - type EventListeners = (); + type EventListeners = Pools; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 10ae7d0c6f74..ea69dbc465ba 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -574,7 +574,7 @@ mod staking_integration { mod pool_integration { use super::*; - use pallet_nomination_pools::{BondExtra, PoolState}; + use pallet_nomination_pools::{BondExtra, BondedPools, PoolState}; use sp_runtime::print; #[test] @@ -899,7 +899,12 @@ mod pool_integration { assert_eq!( pool_events_since_last_call(), vec![ - PoolsEvent::Withdrawn { member: creator, pool_id, balance: creator_stake, points: creator_stake }, + PoolsEvent::Withdrawn { + member: creator, + pool_id, + balance: creator_stake, + points: creator_stake, + }, PoolsEvent::MemberRemoved { pool_id, member: creator }, PoolsEvent::Destroyed { pool_id }, ] @@ -907,19 +912,54 @@ mod pool_integration { }); } - #[test] - fn chill_pool() { - ExtBuilder::default().build_and_execute(|| {}); - } - - #[test] - fn claim_commission_pool_operator() { - ExtBuilder::default().build_and_execute(|| {}); - } - #[test] fn pool_slashed() { - ExtBuilder::default().build_and_execute(|| {}); + ExtBuilder::default().build_and_execute(|| { + start_era(1); + let creator = 100; + let creator_stake = 500; + let pool_id = create_pool(creator, creator_stake); + let delegator_stake = 100; + add_delegators_to_pool(pool_id, (300..306).collect(), delegator_stake); + let pool_acc = Pools::create_bonded_account(pool_id); + + let total_staked = creator_stake + delegator_stake * 6; + assert_eq!(Staking::stake(&pool_acc).unwrap().total, total_staked); + + // lets unbond a delegator each in next eras (2, 3, 4). + start_era(2); + assert_ok!(Pools::unbond(RawOrigin::Signed(300).into(), 300, delegator_stake)); + + start_era(3); + assert_ok!(Pools::unbond(RawOrigin::Signed(301).into(), 301, delegator_stake)); + + start_era(4); + assert_ok!(Pools::unbond(RawOrigin::Signed(302).into(), 302, delegator_stake)); + System::reset_events(); + + // slash the pool at era 3 + assert_eq!(BondedPools::::get(1).unwrap().points, creator_stake + delegator_stake * 6 - delegator_stake * 3); + pallet_staking::slashing::do_slash::( + &pool_acc, + 500, + &mut Default::default(), + &mut Default::default(), + 3, + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + // 301 did not get slashed as all as it unbonded in an era before slash. + // 302 got slashed 50% of 100 = 50. + PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 6, balance: 50 }, + // 303 got slashed 50% of 100 = 50. + PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 7, balance: 50 }, + // Rest of the pool slashed 50% of 800 = 400. + PoolsEvent::PoolSlashed { pool_id: 1, balance: 400 } + ] + ); + }); } fn create_pool(creator: AccountId, amount: Balance) -> u32 { diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index b36f66d634d6..bed0b9f621d9 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -125,7 +125,7 @@ impl SlashingSpans { pub(crate) fn end_span(&mut self, now: EraIndex) -> bool { let next_start = now.defensive_saturating_add(1); if next_start <= self.last_start { - return false + return false; } let last_length = next_start.defensive_saturating_sub(self.last_start); @@ -242,7 +242,7 @@ pub(crate) fn compute_slash( // kick out the validator even if they won't be slashed, // as long as the misbehavior is from their most recent slashing span. kick_out_if_recent::(params); - return None + return None; } let prior_slash_p = ValidatorSlashInEra::::get(¶ms.slash_era, params.stash) @@ -264,7 +264,7 @@ pub(crate) fn compute_slash( // pays out some reward even if the latest report is not max-in-era. // we opt to avoid the nominator lookups and edits and leave more rewards // for more drastic misbehavior. - return None + return None; } // apply slash to validator. @@ -542,7 +542,7 @@ impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> { fn drop(&mut self) { // only update on disk if we slashed this account. if !self.dirty { - return + return; } if let Some((start, end)) = self.spans.prune(self.window_start) { @@ -626,17 +626,14 @@ pub fn do_slash( // deduct overslash from the reward payout *reward_payout = reward_payout.saturating_sub(missing); } + } - let _ = ledger - .update() - .defensive_proof("ledger fetched from storage so it exists in storage; qed."); + let _ = ledger + .update() + .defensive_proof("ledger fetched from storage so it exists in storage; qed."); - // trigger the event - >::deposit_event(super::Event::::Slashed { - staker: stash.clone(), - amount: value, - }); - } + // trigger the event + >::deposit_event(super::Event::::Slashed { staker: stash.clone(), amount: value }); } /// Apply a previously-unapplied slash. @@ -678,7 +675,7 @@ fn pay_reporters( // nobody to pay out to or nothing to pay; // just treat the whole value as slashed. T::Slash::on_unbalanced(slashed_imbalance); - return + return; } // take rewards out of the slashed imbalance. From ff300ef0055ec32cf853e7a7123e710a76d59d7d Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 21:46:26 +0100 Subject: [PATCH 139/202] failing slash test --- substrate/frame/delegated-staking/src/lib.rs | 2 + .../frame/delegated-staking/src/tests.rs | 120 +++++++++++++----- 2 files changed, 87 insertions(+), 35 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index c9ae4d716028..4f6b9a8b21e0 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -177,6 +177,7 @@ pub mod pallet { pub enum Event { Delegated { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, Withdrawn { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Slashed { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, } /// Map of Delegators to their delegation, i.e. (delegate, delegation_amount). @@ -619,6 +620,7 @@ impl Pallet { #[cfg(any(test, feature = "try-runtime"))] use sp_std::collections::btree_map::BTreeMap; + #[cfg(any(test, feature = "try-runtime"))] impl Pallet { pub(crate) fn do_try_state() -> Result<(), TryRuntimeError> { diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index ea69dbc465ba..e68f018368f4 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -915,51 +915,101 @@ mod pool_integration { #[test] fn pool_slashed() { ExtBuilder::default().build_and_execute(|| { - start_era(1); - let creator = 100; - let creator_stake = 500; - let pool_id = create_pool(creator, creator_stake); - let delegator_stake = 100; - add_delegators_to_pool(pool_id, (300..306).collect(), delegator_stake); - let pool_acc = Pools::create_bonded_account(pool_id); - - let total_staked = creator_stake + delegator_stake * 6; - assert_eq!(Staking::stake(&pool_acc).unwrap().total, total_staked); - - // lets unbond a delegator each in next eras (2, 3, 4). - start_era(2); - assert_ok!(Pools::unbond(RawOrigin::Signed(300).into(), 300, delegator_stake)); - - start_era(3); - assert_ok!(Pools::unbond(RawOrigin::Signed(301).into(), 301, delegator_stake)); - - start_era(4); - assert_ok!(Pools::unbond(RawOrigin::Signed(302).into(), 302, delegator_stake)); - System::reset_events(); + start_era(1); + let creator = 100; + let creator_stake = 500; + let pool_id = create_pool(creator, creator_stake); + let delegator_stake = 100; + add_delegators_to_pool(pool_id, (300..306).collect(), delegator_stake); + let pool_acc = Pools::create_bonded_account(pool_id); - // slash the pool at era 3 - assert_eq!(BondedPools::::get(1).unwrap().points, creator_stake + delegator_stake * 6 - delegator_stake * 3); - pallet_staking::slashing::do_slash::( - &pool_acc, - 500, - &mut Default::default(), - &mut Default::default(), - 3, - ); + let total_staked = creator_stake + delegator_stake * 6; + assert_eq!(Staking::stake(&pool_acc).unwrap().total, total_staked); - assert_eq!( - pool_events_since_last_call(), - vec![ + // lets unbond a delegator each in next eras (2, 3, 4). + start_era(2); + assert_ok!(Pools::unbond(RawOrigin::Signed(300).into(), 300, delegator_stake)); + + start_era(3); + assert_ok!(Pools::unbond(RawOrigin::Signed(301).into(), 301, delegator_stake)); + + start_era(4); + assert_ok!(Pools::unbond(RawOrigin::Signed(302).into(), 302, delegator_stake)); + System::reset_events(); + + // slash the pool at era 3 + assert_eq!( + BondedPools::::get(1).unwrap().points, + creator_stake + delegator_stake * 6 - delegator_stake * 3 + ); + pallet_staking::slashing::do_slash::( + &pool_acc, + 500, + &mut Default::default(), + &mut Default::default(), + 3, + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ // 301 did not get slashed as all as it unbonded in an era before slash. // 302 got slashed 50% of 100 = 50. PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 6, balance: 50 }, // 303 got slashed 50% of 100 = 50. PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 7, balance: 50 }, // Rest of the pool slashed 50% of 800 = 400. - PoolsEvent::PoolSlashed { pool_id: 1, balance: 400 } + PoolsEvent::PoolSlashed { pool_id: 1, balance: 400 }, ] + ); + + // slash is lazy and balance is still locked in user's accounts. + assert_eq!(held_balance(&creator), creator_stake); + for i in 300..306 { + assert_eq!(held_balance(&i), delegator_stake); + } + assert_eq!( + get_pool_delegate(pool_id).ledger.effective_balance(), + Staking::total_stake(&pool_acc).unwrap() + ); + + // pending slash is book kept. + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); + + // go in some distant future era. + start_era(10); + System::reset_events(); + + // 300 is not slashed and can withdraw all balance. + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(300).into(), 300, 1)); + assert_eq!( + events_since_last_call(), + vec![ + Event::Withdrawn { delegate: pool_acc, delegator: 300, amount: 100 }, + ] + ); + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); + + let pre_301 = Balances::free_balance(301); + // 301 is slashed and should only be able to withdraw 50. + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 1)); + assert_eq!( + events_since_last_call(), + vec![ + // half amount is slashed first. + Event::Slashed { delegate: pool_acc, delegator: 301, amount: 50 }, + // rest of it is withdrawn. + Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 }, + ] ); - }); + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 450); + assert_eq!(held_balance(&301), 0); + assert_eq!(Balances::free_balance(301) - pre_301, 50); + + // delegators cannot unbond without applying pending slash on their accounts. + + // when unbonding, should apply slash. Make sure numbers are good. + }); } fn create_pool(creator: AccountId, amount: Balance) -> u32 { From c19bf20ff8c24abca812aaab54d6f9b76d30ff11 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 22:17:00 +0100 Subject: [PATCH 140/202] add slash call --- .../frame/delegated-staking/src/impls.rs | 51 +++++-------------- substrate/frame/delegated-staking/src/lib.rs | 48 +++++++++++++++++ .../frame/delegated-staking/src/tests.rs | 8 ++- .../frame/nomination-pools/src/adapter.rs | 5 ++ .../primitives/staking/src/delegation.rs | 6 +++ 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 6185c1f0797d..cc0c102f7282 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -196,43 +196,6 @@ impl StakingInterface for Pallet { } } -// impl DelegationInterface for Pallet { -// FIXME(ank4n): Should be part of NP Adapter. -// fn apply_slash( -// delegate: &Self::AccountId, -// delegator: &Self::AccountId, -// value: Self::Balance, -// maybe_reporter: Option, -// ) -> DispatchResult { -// let mut delegation_register = -// >::get(delegate).ok_or(Error::::NotDelegate)?; -// let delegation = >::get(delegator).ok_or(Error::::NotDelegator)?; -// -// ensure!(&delegation.delegate == delegate, Error::::NotDelegate); -// ensure!(delegation.amount >= value, Error::::NotEnoughFunds); -// -// let (mut credit, _missing) = -// T::Currency::slash(&HoldReason::Delegating.into(), &delegator, value); -// let actual_slash = credit.peek(); -// // remove the slashed amount -// delegation_register.pending_slash.saturating_reduce(actual_slash); -// >::insert(delegate, delegation_register); -// -// if let Some(reporter) = maybe_reporter { -// let reward_payout: BalanceOf = -// T::CoreStaking::slash_reward_fraction() * actual_slash; -// let (reporter_reward, rest) = credit.split(reward_payout); -// credit = rest; -// // fixme(ank4n): handle error -// let _ = T::Currency::resolve(&reporter, reporter_reward); -// } -// -// T::OnSlash::on_unbalanced(credit); -// Ok(()) -// } -// -// } - impl StakingDelegationSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -347,4 +310,18 @@ impl PoolAdapter for Pallet { // fixme(ank4n): This should not require slashing spans. Pallet::::release(RawOrigin::Signed(pool_account.clone()).into(), who.clone(), amount, 0) } + + fn apply_slash( + delegate: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> DispatchResult { + Pallet::::slash( + RawOrigin::Signed(delegate.clone()).into(), + delegator.clone(), + value, + maybe_reporter, + ) + } } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 4f6b9a8b21e0..88380f4a3bda 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -340,6 +340,54 @@ pub mod pallet { Ok(()) } + + /// Apply a pending slash. + /// + /// Delegate calls this to apply a pending slash to a delegator. + #[pallet::call_index(6)] + #[pallet::weight(Weight::default())] + pub fn slash( + origin: OriginFor, + delegator: T::AccountId, + amount: BalanceOf, + maybe_reporter: Option, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut delegate = Delegate::::from(&who)?; + let mut delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; + + ensure!(delegation.delegate == who, Error::::NotDelegate); + ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); + + let (mut credit, missing) = + T::Currency::slash(&HoldReason::Delegating.into(), &delegator, amount); + + defensive_assert!(missing.is_zero(), "slash should have been fully applied"); + + let actual_slash = credit.peek(); + + // remove the slashed amount + delegate.ledger.pending_slash.saturating_reduce(actual_slash); + delegate.save(); + + delegation + .decrease_delegation(actual_slash) + .ok_or(ArithmeticError::Overflow)? + .save(&delegator); + + if let Some(reporter) = maybe_reporter { + let reward_payout: BalanceOf = + T::CoreStaking::slash_reward_fraction() * actual_slash; + let (reporter_reward, rest) = credit.split(reward_payout); + credit = rest; + + // fixme(ank4n): handle error + let _ = T::Currency::resolve(&reporter, reporter_reward); + } + + T::OnSlash::on_unbalanced(credit); + Ok(()) + } } #[pallet::hooks] diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index e68f018368f4..178a53a605e2 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -575,7 +575,6 @@ mod staking_integration { mod pool_integration { use super::*; use pallet_nomination_pools::{BondExtra, BondedPools, PoolState}; - use sp_runtime::print; #[test] fn create_pool_test() { @@ -909,6 +908,13 @@ mod pool_integration { PoolsEvent::Destroyed { pool_id }, ] ); + + // Make sure all data is cleaned up. + assert_eq!(Delegates::::contains_key(Pools::create_bonded_account(pool_id)), false); + assert_eq!(Delegators::::contains_key(creator), false); + for i in 300..310 { + assert_eq!(Delegators::::contains_key(i), false); + } }); } diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 3208576d2eee..e798de0d2502 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -68,4 +68,9 @@ impl PoolAdapter for NoDelegation { Ok(()) } + + fn apply_slash(_delegate: &Self::AccountId, _delegator: &Self::AccountId, _value: Self::Balance, _maybe_reporter: Option) -> sp_runtime::DispatchResult { + // for direct staking, slashing is eager, and we don't need to do anything here. + Ok(()) + } } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 683e89836317..ea59fd352285 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -201,4 +201,10 @@ pub trait PoolAdapter { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; + fn apply_slash( + delegate: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> DispatchResult; } From 66c190e88dc04315e8e97c48fb040a9b0ea8f097 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 23 Feb 2024 22:17:18 +0100 Subject: [PATCH 141/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 3 +- .../frame/delegated-staking/src/tests.rs | 48 +++++++++---------- .../frame/nomination-pools/src/adapter.rs | 7 ++- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 88380f4a3bda..eb9b62aa841e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -354,7 +354,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut delegate = Delegate::::from(&who)?; - let mut delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; + let mut delegation = + >::get(&delegator).ok_or(Error::::NotDelegator)?; ensure!(delegation.delegate == who, Error::::NotDelegate); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 178a53a605e2..14f5e4d46c0d 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -979,38 +979,36 @@ mod pool_integration { Staking::total_stake(&pool_acc).unwrap() ); - // pending slash is book kept. + // pending slash is book kept. assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); - // go in some distant future era. - start_era(10); - System::reset_events(); + // go in some distant future era. + start_era(10); + System::reset_events(); - // 300 is not slashed and can withdraw all balance. - assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(300).into(), 300, 1)); - assert_eq!( - events_since_last_call(), - vec![ - Event::Withdrawn { delegate: pool_acc, delegator: 300, amount: 100 }, - ] - ); - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); + // 300 is not slashed and can withdraw all balance. + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(300).into(), 300, 1)); + assert_eq!( + events_since_last_call(), + vec![Event::Withdrawn { delegate: pool_acc, delegator: 300, amount: 100 },] + ); + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); let pre_301 = Balances::free_balance(301); - // 301 is slashed and should only be able to withdraw 50. - assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 1)); - assert_eq!( - events_since_last_call(), - vec![ + // 301 is slashed and should only be able to withdraw 50. + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 1)); + assert_eq!( + events_since_last_call(), + vec![ // half amount is slashed first. - Event::Slashed { delegate: pool_acc, delegator: 301, amount: 50 }, + Event::Slashed { delegate: pool_acc, delegator: 301, amount: 50 }, // rest of it is withdrawn. - Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 }, - ] - ); - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 450); - assert_eq!(held_balance(&301), 0); - assert_eq!(Balances::free_balance(301) - pre_301, 50); + Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 }, + ] + ); + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 450); + assert_eq!(held_balance(&301), 0); + assert_eq!(Balances::free_balance(301) - pre_301, 50); // delegators cannot unbond without applying pending slash on their accounts. diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index e798de0d2502..ad79270c59d3 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -69,7 +69,12 @@ impl PoolAdapter for NoDelegation { Ok(()) } - fn apply_slash(_delegate: &Self::AccountId, _delegator: &Self::AccountId, _value: Self::Balance, _maybe_reporter: Option) -> sp_runtime::DispatchResult { + fn apply_slash( + _delegate: &Self::AccountId, + _delegator: &Self::AccountId, + _value: Self::Balance, + _maybe_reporter: Option, + ) -> sp_runtime::DispatchResult { // for direct staking, slashing is eager, and we don't need to do anything here. Ok(()) } From 9af4ae1dc11141ed90f8153c4ecbfc246346ce28 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 24 Feb 2024 19:42:15 +0100 Subject: [PATCH 142/202] slash extrinsic in np --- .../frame/delegated-staking/src/impls.rs | 15 ++- substrate/frame/delegated-staking/src/lib.rs | 99 +++++++------- .../frame/delegated-staking/src/tests.rs | 1 + .../frame/nomination-pools/src/adapter.rs | 2 +- substrate/frame/nomination-pools/src/lib.rs | 125 ++++++++++++------ .../primitives/staking/src/delegation.rs | 8 +- 6 files changed, 151 insertions(+), 99 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index cc0c102f7282..9f6c5d646a87 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -258,10 +258,15 @@ impl PoolAdapter for Pallet { .unwrap_or(Zero::zero()) } - /// Returns balance of `Delegate` account including the held balances. + /// Returns balance of account that is held. /// - /// Equivalent to [FunInspect::total_balance] for non delegate accounts. + /// - For [Delegate] accounts, this is their total delegation amount. + /// - For [Delegators], this is their delegation amount. fn total_balance(who: &Self::AccountId) -> Self::Balance { + if Self::is_delegator(who) { + return Delegation::::get(who).map(|d| d.amount).unwrap_or(Zero::zero()); + } + Delegate::::from(who) .map(|delegate| delegate.ledger.effective_balance()) .unwrap_or(Zero::zero()) @@ -311,14 +316,14 @@ impl PoolAdapter for Pallet { Pallet::::release(RawOrigin::Signed(pool_account.clone()).into(), who.clone(), amount, 0) } - fn apply_slash( + fn delegator_slash( delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option, ) -> DispatchResult { - Pallet::::slash( - RawOrigin::Signed(delegate.clone()).into(), + Pallet::::do_slash( + delegate.clone(), delegator.clone(), value, maybe_reporter, diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index eb9b62aa841e..735630784f59 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -340,55 +340,6 @@ pub mod pallet { Ok(()) } - - /// Apply a pending slash. - /// - /// Delegate calls this to apply a pending slash to a delegator. - #[pallet::call_index(6)] - #[pallet::weight(Weight::default())] - pub fn slash( - origin: OriginFor, - delegator: T::AccountId, - amount: BalanceOf, - maybe_reporter: Option, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let mut delegate = Delegate::::from(&who)?; - let mut delegation = - >::get(&delegator).ok_or(Error::::NotDelegator)?; - - ensure!(delegation.delegate == who, Error::::NotDelegate); - ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); - - let (mut credit, missing) = - T::Currency::slash(&HoldReason::Delegating.into(), &delegator, amount); - - defensive_assert!(missing.is_zero(), "slash should have been fully applied"); - - let actual_slash = credit.peek(); - - // remove the slashed amount - delegate.ledger.pending_slash.saturating_reduce(actual_slash); - delegate.save(); - - delegation - .decrease_delegation(actual_slash) - .ok_or(ArithmeticError::Overflow)? - .save(&delegator); - - if let Some(reporter) = maybe_reporter { - let reward_payout: BalanceOf = - T::CoreStaking::slash_reward_fraction() * actual_slash; - let (reporter_reward, rest) = credit.split(reward_payout); - credit = rest; - - // fixme(ank4n): handle error - let _ = T::Currency::resolve(&reporter, reporter_reward); - } - - T::OnSlash::on_unbalanced(credit); - Ok(()) - } } #[pallet::hooks] @@ -665,6 +616,56 @@ impl Pallet { Ok(()) } + + pub fn do_slash( + delegate_acc: T::AccountId, + delegator: T::AccountId, + amount: BalanceOf, + maybe_reporter: Option, + ) -> DispatchResult { + let mut delegate = Delegate::::from(&delegate_acc)?; + let mut delegation = + >::get(&delegator).ok_or(Error::::NotDelegator)?; + + ensure!(delegation.delegate == delegate_acc, Error::::NotDelegate); + ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); + + let (mut credit, missing) = + T::Currency::slash(&HoldReason::Delegating.into(), &delegator, amount); + + defensive_assert!(missing.is_zero(), "slash should have been fully applied"); + + let actual_slash = credit.peek(); + + // remove the slashed amount + delegate.ledger.pending_slash.saturating_reduce(actual_slash); + delegate.save(); + + delegation + .decrease_delegation(actual_slash) + .ok_or(ArithmeticError::Overflow)? + .save(&delegator); + + if let Some(reporter) = maybe_reporter { + let reward_payout: BalanceOf = + T::CoreStaking::slash_reward_fraction() * actual_slash; + let (reporter_reward, rest) = credit.split(reward_payout); + credit = rest; + + // fixme(ank4n): handle error + let _ = T::Currency::resolve(&reporter, reporter_reward); + } + + T::OnSlash::on_unbalanced(credit); + + Self::deposit_event(Event::::Slashed { + delegate: delegate_acc, + delegator, + amount, + }); + + Ok(()) + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 14f5e4d46c0d..bd5234cbd810 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -1011,6 +1011,7 @@ mod pool_integration { assert_eq!(Balances::free_balance(301) - pre_301, 50); // delegators cannot unbond without applying pending slash on their accounts. + // slash_amount = total balance held - (balance from active pool + balance from unbonding pool) // when unbonding, should apply slash. Make sure numbers are good. }); diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index ad79270c59d3..19ee78ddd637 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -69,7 +69,7 @@ impl PoolAdapter for NoDelegation { Ok(()) } - fn apply_slash( + fn delegator_slash( _delegate: &Self::AccountId, _delegator: &Self::AccountId, _value: Self::Balance, diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 08de180e2d8e..7bffb910f534 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -546,9 +546,14 @@ impl PoolMember { /// Total balance of the member, both active and unbonding. /// Doesn't mutate state. - #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] fn total_balance(&self) -> BalanceOf { - let pool = BondedPool::::get(self.pool_id).unwrap(); + let maybe_pool = BondedPool::::get(self.pool_id); + if maybe_pool.is_none() { + defensive!("pool should exist; qed"); + return Zero::zero(); + } + let pool = maybe_pool.expect("checked pool is not none; qed"); + let active_balance = pool.points_to_balance(self.active_points()); let sub_pools = match SubPoolsStorage::::get(self.pool_id) { @@ -737,13 +742,13 @@ impl Commission { // do not throttle if `to` is the same or a decrease in commission. if *to <= commission_as_percent { - return false + return false; } // Test for `max_increase` throttling. // // Throttled if the attempted increase in commission is greater than `max_increase`. if (*to).saturating_sub(commission_as_percent) > t.max_increase { - return true + return true; } // Test for `min_delay` throttling. @@ -766,7 +771,7 @@ impl Commission { blocks_surpassed < t.min_delay } }, - ) + ); } false } @@ -824,7 +829,7 @@ impl Commission { ); if let Some(old) = self.max.as_mut() { if new_max > *old { - return Err(Error::::MaxCommissionRestricted.into()) + return Err(Error::::MaxCommissionRestricted.into()); } *old = new_max; } else { @@ -1210,7 +1215,7 @@ impl BondedPool { }, (false, true) => { // the depositor can simply not be unbonded permissionlessly, period. - return Err(Error::::DoesNotHavePermission.into()) + return Err(Error::::DoesNotHavePermission.into()); }, }; @@ -1780,7 +1785,7 @@ pub mod pallet { /// Events of this pallet. #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] + #[pallet::generate_deposit(pub (crate) fn deposit_event)] pub enum Event { /// A pool has been created. Created { depositor: T::AccountId, pool_id: PoolId }, @@ -1934,6 +1939,8 @@ pub mod pallet { BondExtraRestricted, /// No imbalance in the ED deposit for the pool. NothingToAdjust, + /// No slash pending that can be applied to the member. + NothingToSlash, } #[derive(Encode, Decode, PartialEq, TypeInfo, PalletError, RuntimeDebug)] @@ -2042,9 +2049,9 @@ pub mod pallet { // of just once, in the spirit reusing code. #[pallet::call_index(1)] #[pallet::weight( - T::WeightInfo::bond_extra_transfer() - .max(T::WeightInfo::bond_extra_other()) - )] + T::WeightInfo::bond_extra_transfer() + .max(T::WeightInfo::bond_extra_other()) + )] pub fn bond_extra(origin: OriginFor, extra: BondExtra>) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_bond_extra(who.clone(), who, extra) @@ -2181,7 +2188,7 @@ pub mod pallet { /// would probably see an error like `NoMoreChunks` emitted from the staking system when /// they attempt to unbond. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::pool_withdraw_unbonded(*num_slashing_spans))] + #[pallet::weight(T::WeightInfo::pool_withdraw_unbonded(* num_slashing_spans))] pub fn pool_withdraw_unbonded( origin: OriginFor, pool_id: PoolId, @@ -2219,8 +2226,8 @@ pub mod pallet { /// If the target is the depositor, the pool will be destroyed. #[pallet::call_index(5)] #[pallet::weight( - T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans) - )] + T::WeightInfo::withdraw_unbonded_kill(* num_slashing_spans) + )] pub fn withdraw_unbonded( origin: OriginFor, member_account: AccountIdLookupOf, @@ -2588,9 +2595,9 @@ pub mod pallet { /// `PermissionlessAll` or `PermissionlessCompound`. #[pallet::call_index(14)] #[pallet::weight( - T::WeightInfo::bond_extra_transfer() - .max(T::WeightInfo::bond_extra_other()) - )] + T::WeightInfo::bond_extra_transfer() + .max(T::WeightInfo::bond_extra_other()) + )] pub fn bond_extra_other( origin: OriginFor, member: AccountIdLookupOf, @@ -2774,6 +2781,19 @@ pub mod pallet { Ok(()) } + + /// Apply a pending slash on a member. + #[pallet::call_index(23)] + // FIXME(ank4n): fix weight info. + #[pallet::weight(T::WeightInfo::set_commission_claim_permission())] + pub fn apply_slash( + origin: OriginFor, + member_account: AccountIdLookupOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let member_account = T::Lookup::lookup(member_account)?; + Self::do_apply_slash(member_account, Some(who)) + } } #[pallet::hooks] @@ -2962,7 +2982,7 @@ impl Pallet { let balance = T::U256ToBalance::convert; if current_balance.is_zero() || current_points.is_zero() || points.is_zero() { // There is nothing to unbond - return Zero::zero() + return Zero::zero(); } // Equivalent of (current_balance / current_points) * points @@ -2999,7 +3019,7 @@ impl Pallet { // will be zero. let pending_rewards = member.pending_rewards(current_reward_counter)?; if pending_rewards.is_zero() { - return Ok(pending_rewards) + return Ok(pending_rewards); } // IFF the reward is non-zero alter the member and reward pool info. @@ -3227,7 +3247,7 @@ impl Pallet { let min_balance = T::Currency::minimum_balance(); if pre_frozen_balance == min_balance { - return Err(Error::::NothingToAdjust.into()) + return Err(Error::::NothingToAdjust.into()); } // Update frozen amount with current ED. @@ -3254,6 +3274,25 @@ impl Pallet { Ok(()) } + fn do_apply_slash(member_account: T::AccountId, reporter: Option) -> DispatchResult { + // calculate points to be slashed. + let member = + PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; + let bonded_pool = Self::create_bonded_account(member.pool_id); + + let delegated_balance = T::PoolAdapter::total_balance(&member_account); + let current_balance = member.total_balance(); + defensive_assert!( + delegated_balance >= current_balance, + "delegated balance should always be greater or equal to current balance" + ); + + // if nothing to slash, return error. + ensure!(delegated_balance > current_balance, Error::::NothingToSlash); + + T::PoolAdapter::delegator_slash(&bonded_pool, &member_account, delegated_balance.defensive_saturating_sub(current_balance), reporter) + } + /// Apply freeze on reward account to restrict it from going below ED. pub(crate) fn freeze_pool_deposit(reward_acc: &T::AccountId) -> DispatchResult { T::Currency::set_freeze( @@ -3306,7 +3345,7 @@ impl Pallet { #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] pub fn do_try_state(level: u8) -> Result<(), TryRuntimeError> { if level.is_zero() { - return Ok(()) + return Ok(()); } // note: while a bit wacky, since they have the same key, even collecting to vec should // result in the same set of keys, in the same order. @@ -3439,7 +3478,7 @@ impl Pallet { ); if level <= 1 { - return Ok(()) + return Ok(()); } for (pool_id, _pool) in BondedPools::::iter() { @@ -3451,14 +3490,14 @@ impl Pallet { let total_balance = T::PoolAdapter::total_balance(&pool_account); assert!( - total_balance >= bonded_balance + sum_unbonding_balance, - "faulty pool: {:?} / {:?}, total_balance {:?} >= bonded_balance {:?} + sum_unbonding_balance {:?}", - pool_id, - _pool, - total_balance, - bonded_balance, - sum_unbonding_balance - ); + total_balance >= bonded_balance + sum_unbonding_balance, + "faulty pool: {:?} / {:?}, total_balance {:?} >= bonded_balance {:?} + sum_unbonding_balance {:?}", + pool_id, + _pool, + total_balance, + bonded_balance, + sum_unbonding_balance + ); } // Warn if any pool has incorrect ED frozen. We don't want to fail hard as this could be a @@ -3481,21 +3520,21 @@ impl Pallet { pub fn check_ed_imbalance() -> Result<(), DispatchError> { let mut failed: u32 = 0; BondedPools::::iter_keys().for_each(|id| { - let reward_acc = Self::create_reward_account(id); - let frozen_balance = - T::Currency::balance_frozen(&FreezeReason::PoolMinBalance.into(), &reward_acc); - - let expected_frozen_balance = T::Currency::minimum_balance(); - if frozen_balance != expected_frozen_balance { - failed += 1; - log::warn!( + let reward_acc = Self::create_reward_account(id); + let frozen_balance = + T::Currency::balance_frozen(&FreezeReason::PoolMinBalance.into(), &reward_acc); + + let expected_frozen_balance = T::Currency::minimum_balance(); + if frozen_balance != expected_frozen_balance { + failed += 1; + log::warn!( "pool {:?} has incorrect ED frozen that can result from change in ED. Expected = {:?}, Actual = {:?}", id, expected_frozen_balance, frozen_balance, ); - } - }); + } + }); ensure!(failed == 0, "Some pools do not have correct ED frozen"); Ok(()) @@ -3528,7 +3567,7 @@ impl Pallet { let (current_reward_counter, _) = reward_pool .current_reward_counter(pool_member.pool_id, bonded_pool.points, commission) .ok()?; - return pool_member.pending_rewards(current_reward_counter).ok() + return pool_member.pending_rewards(current_reward_counter).ok(); } } @@ -3574,7 +3613,9 @@ impl sp_staking::OnStakingUpdate> for Pall slashed_unlocking: &BTreeMap>, total_slashed: BalanceOf, ) { - let Some(pool_id) = ReversePoolIdLookup::::get(pool_account) else { return }; + let Some(pool_id) = ReversePoolIdLookup::::get(pool_account) else { + return; + }; // As the slashed account belongs to a `BondedPool` the `TotalValueLocked` decreases and // an event is emitted. TotalValueLocked::::mutate(|tvl| { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index ea59fd352285..18c01c47dd1b 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -175,7 +175,7 @@ pub trait PoolAdapter { /// Balance that is free and can be released to delegator. fn releasable_balance(who: &Self::AccountId) -> Self::Balance; - /// Similar to [Inspect::total_balance]. + /// Total balance of the account held for staking. fn total_balance(who: &Self::AccountId) -> Self::Balance; /// Initiate delegation to the pool account. @@ -201,7 +201,11 @@ pub trait PoolAdapter { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - fn apply_slash( + + /// Apply a slash to the `delegator`. + /// + /// This is called when the corresponding `delegate` has pending slash to be applied. + fn delegator_slash( delegate: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, From 2049a7c5c0ee67d0713d54a1e78793c185877eb5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 24 Feb 2024 19:43:56 +0100 Subject: [PATCH 143/202] fmt --- substrate/frame/delegated-staking/src/impls.rs | 7 +------ substrate/frame/delegated-staking/src/lib.rs | 9 ++------- substrate/frame/delegated-staking/src/tests.rs | 3 ++- substrate/frame/nomination-pools/src/lib.rs | 18 +++++++++++++----- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 9f6c5d646a87..176e2659b720 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -322,11 +322,6 @@ impl PoolAdapter for Pallet { value: Self::Balance, maybe_reporter: Option, ) -> DispatchResult { - Pallet::::do_slash( - delegate.clone(), - delegator.clone(), - value, - maybe_reporter, - ) + Pallet::::do_slash(delegate.clone(), delegator.clone(), value, maybe_reporter) } } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 735630784f59..201fd6ea920d 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -624,8 +624,7 @@ impl Pallet { maybe_reporter: Option, ) -> DispatchResult { let mut delegate = Delegate::::from(&delegate_acc)?; - let mut delegation = - >::get(&delegator).ok_or(Error::::NotDelegator)?; + let mut delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; ensure!(delegation.delegate == delegate_acc, Error::::NotDelegate); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); @@ -658,11 +657,7 @@ impl Pallet { T::OnSlash::on_unbalanced(credit); - Self::deposit_event(Event::::Slashed { - delegate: delegate_acc, - delegator, - amount, - }); + Self::deposit_event(Event::::Slashed { delegate: delegate_acc, delegator, amount }); Ok(()) } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index bd5234cbd810..5a73af747cb1 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -1011,7 +1011,8 @@ mod pool_integration { assert_eq!(Balances::free_balance(301) - pre_301, 50); // delegators cannot unbond without applying pending slash on their accounts. - // slash_amount = total balance held - (balance from active pool + balance from unbonding pool) + // slash_amount = total balance held - (balance from active pool + balance from + // unbonding pool) // when unbonding, should apply slash. Make sure numbers are good. }); diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 7bffb910f534..6b76355ca6b5 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1939,8 +1939,8 @@ pub mod pallet { BondExtraRestricted, /// No imbalance in the ED deposit for the pool. NothingToAdjust, - /// No slash pending that can be applied to the member. - NothingToSlash, + /// No slash pending that can be applied to the member. + NothingToSlash, } #[derive(Encode, Decode, PartialEq, TypeInfo, PalletError, RuntimeDebug)] @@ -3274,7 +3274,10 @@ impl Pallet { Ok(()) } - fn do_apply_slash(member_account: T::AccountId, reporter: Option) -> DispatchResult { + fn do_apply_slash( + member_account: T::AccountId, + reporter: Option, + ) -> DispatchResult { // calculate points to be slashed. let member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; @@ -3287,10 +3290,15 @@ impl Pallet { "delegated balance should always be greater or equal to current balance" ); - // if nothing to slash, return error. + // if nothing to slash, return error. ensure!(delegated_balance > current_balance, Error::::NothingToSlash); - T::PoolAdapter::delegator_slash(&bonded_pool, &member_account, delegated_balance.defensive_saturating_sub(current_balance), reporter) + T::PoolAdapter::delegator_slash( + &bonded_pool, + &member_account, + delegated_balance.defensive_saturating_sub(current_balance), + reporter, + ) } /// Apply freeze on reward account to restrict it from going below ED. From 1d31384fcf93062f77907a688a3981a76edc7f1b Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 24 Feb 2024 20:09:56 +0100 Subject: [PATCH 144/202] slash works --- .../frame/delegated-staking/src/impls.rs | 6 + substrate/frame/delegated-staking/src/lib.rs | 4 +- .../frame/nomination-pools/src/adapter.rs | 8 +- substrate/frame/nomination-pools/src/lib.rs | 18 ++- .../primitives/staking/src/delegation.rs | 107 +----------------- 5 files changed, 32 insertions(+), 111 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 176e2659b720..c49e3ff90ea4 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -316,6 +316,12 @@ impl PoolAdapter for Pallet { Pallet::::release(RawOrigin::Signed(pool_account.clone()).into(), who.clone(), amount, 0) } + fn has_pending_slash(delegate: &Self::AccountId) -> bool { + Delegate::::from(delegate) + .map(|d| !d.ledger.pending_slash.is_zero()) + .unwrap_or(false) + } + fn delegator_slash( delegate: &Self::AccountId, delegator: &Self::AccountId, diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 201fd6ea920d..04ee1f3525d7 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -83,8 +83,7 @@ use sp_runtime::{ ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, TryRuntimeError, }; use sp_staking::{ - delegation::{DelegationInterface, StakingDelegationSupport}, - EraIndex, Stake, StakerStatus, StakingInterface, + delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -638,6 +637,7 @@ impl Pallet { // remove the slashed amount delegate.ledger.pending_slash.saturating_reduce(actual_slash); + delegate.ledger.total_delegated.saturating_reduce(actual_slash); delegate.save(); delegation diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 19ee78ddd637..0fbb5826f145 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -69,6 +69,11 @@ impl PoolAdapter for NoDelegation { Ok(()) } + fn has_pending_slash(delegate: &Self::AccountId) -> bool { + // for direct staking, slashing is eager, and we don't need to do anything here. + false + } + fn delegator_slash( _delegate: &Self::AccountId, _delegator: &Self::AccountId, @@ -76,6 +81,7 @@ impl PoolAdapter for NoDelegation { _maybe_reporter: Option, ) -> sp_runtime::DispatchResult { // for direct staking, slashing is eager, and we don't need to do anything here. - Ok(()) + defensive!("Delegator slash is not supported for direct staking"); + Err(Error::::NothingToSlash.into()) } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 6b76355ca6b5..da160498210b 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1286,6 +1286,10 @@ impl BondedPool { }); }; } + + fn has_pending_slash(&self) -> bool { + T::PoolAdapter::has_pending_slash(&self.bonded_account()) + } } /// A reward pool. @@ -2244,6 +2248,11 @@ pub mod pallet { let mut sub_pools = SubPoolsStorage::::get(member.pool_id).ok_or(Error::::SubPoolsNotFound)?; + if bonded_pool.has_pending_slash(){ + // apply slash if any before withdraw. + let _ = Self::do_apply_slash(&member_account, None); + } + bonded_pool.ok_to_withdraw_unbonded_with(&caller, &member_account)?; // NOTE: must do this after we have done the `ok_to_withdraw_unbonded_other_with` check. @@ -2792,7 +2801,7 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let member_account = T::Lookup::lookup(member_account)?; - Self::do_apply_slash(member_account, Some(who)) + Self::do_apply_slash(&member_account, Some(who)) } } @@ -3275,13 +3284,14 @@ impl Pallet { } fn do_apply_slash( - member_account: T::AccountId, + member_account: &T::AccountId, reporter: Option, ) -> DispatchResult { // calculate points to be slashed. let member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; - let bonded_pool = Self::create_bonded_account(member.pool_id); + let bonded_account = Self::create_bonded_account(member.pool_id); + ensure!(T::PoolAdapter::has_pending_slash(&bonded_account), Error::::NothingToSlash); let delegated_balance = T::PoolAdapter::total_balance(&member_account); let current_balance = member.total_balance(); @@ -3294,7 +3304,7 @@ impl Pallet { ensure!(delegated_balance > current_balance, Error::::NothingToSlash); T::PoolAdapter::delegator_slash( - &bonded_pool, + &bonded_account, &member_account, delegated_balance.defensive_saturating_sub(current_balance), reporter, diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 18c01c47dd1b..eff5a72c1889 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -20,110 +20,6 @@ use scale_info::TypeInfo; use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; -/// Allows an account to accept stake delegations and manage its operations. -// FIXME(ank4n): Remove this and add a new trait (in delegation pallet) for NP adapter. -pub trait DelegationInterface { - /// Balance type used by the staking system. - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - - /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; - - /// Total delegated balance to this account. - fn delegated_balance(who: &Self::AccountId) -> Self::Balance; - - /// Total delegated balance to this account that is not yet bonded to staking. - fn unbonded_balance(who: &Self::AccountId) -> Self::Balance; - - /// Set intention to accept delegations. - fn accept_delegations( - delegate: &Self::AccountId, - reward_destination: &Self::AccountId, - ) -> DispatchResult; - - /// Migrate a nominator account into a `delegate`. - /// - /// # Arguments - /// - /// * `new_delegate`: This is the current nominator account. Funds will be moved from this - /// account to `proxy_delegator` and delegated back to `new_delegate`. - /// * `proxy_delegator`: All existing staked funds will be moved to this account. Future - /// migration of funds from `proxy_delegator` to `delegator` is possible via calling - /// [`Self::migrate_delegator`]. - /// * `payee`: `Delegate` needs to set where they want their rewards to be paid out. This can be - /// anything other than the `delegate` account itself. - /// - /// This is similar to [`Self::accept_delegations`] but allows a current nominator to migrate to - /// a `delegate`. - fn migrate_accept_delegations( - new_delegate: &Self::AccountId, - proxy_delegator: &Self::AccountId, - payee: &Self::AccountId, - ) -> DispatchResult; - - /// Stop accepting new delegations to this account. - fn block_delegations(delegate: &Self::AccountId) -> DispatchResult; - - /// Unblock delegations to this account. - fn unblock_delegations(delegate: &Self::AccountId) -> DispatchResult; - - /// Remove oneself as a `delegate`. - /// - /// This will only succeed if all delegations to this `delegate` are withdrawn. - fn kill_delegate(delegate: &Self::AccountId) -> DispatchResult; - - /// Bond all fund that is delegated but not staked. - /// FIXME(ank4n): Should not be allowed as withdrawn funds would get restaked. - fn bond_all(delegate: &Self::AccountId) -> DispatchResult; - - /// Request withdrawal of unbonded stake of `delegate` belonging to the provided `delegator`. - /// - /// Important: It is upto `delegate` to enforce which `delegator` can withdraw `value`. The - /// withdrawn value is released in `delegator`'s account. - fn delegate_withdraw( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - num_slashing_spans: u32, - ) -> DispatchResult; - - /// Applies a pending slash on `delegate` by passing a delegator account who should be slashed - /// and the value to be slashed. Optionally also takes a reporter account who will be rewarded - /// from part of the slash imbalance. - fn apply_slash( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - reporter: Option, - ) -> DispatchResult; - - /// Move a delegated amount from `proxy_delegator` to `new_delegator`. - /// - /// `Delegate` must have used [`Self::migrate_accept_delegations`] to setup a `proxy_delegator`. - /// This is useful for migrating old pool accounts using direct staking to lazily move - /// delegators to the new delegated pool account. - fn migrate_delegator( - delegate: &Self::AccountId, - new_delegator: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult; - - /// Delegate some funds to a `delegate` account. - fn delegate( - delegator: &Self::AccountId, - delegate: &Self::AccountId, - value: Self::Balance, - ) -> DispatchResult; -} - /// Something that provides delegation support to core staking. pub trait StakingDelegationSupport { /// Balance type used by the staking system. @@ -202,6 +98,9 @@ pub trait PoolAdapter { amount: Self::Balance, ) -> DispatchResult; + /// Returns true if the `delegate` has pending slash to be applied. + fn has_pending_slash(delegate: &Self::AccountId) -> bool; + /// Apply a slash to the `delegator`. /// /// This is called when the corresponding `delegate` has pending slash to be applied. From 7c276abc615ece012e82e94da54f71cabe2ebd91 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 24 Feb 2024 20:10:16 +0100 Subject: [PATCH 145/202] fmt --- substrate/frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index da160498210b..5c084fb30eb9 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -2248,7 +2248,7 @@ pub mod pallet { let mut sub_pools = SubPoolsStorage::::get(member.pool_id).ok_or(Error::::SubPoolsNotFound)?; - if bonded_pool.has_pending_slash(){ + if bonded_pool.has_pending_slash() { // apply slash if any before withdraw. let _ = Self::do_apply_slash(&member_account, None); } From 086d2f68370a02595b172fcfc75415a8498c9ef9 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 24 Feb 2024 21:15:12 +0100 Subject: [PATCH 146/202] finish test for slashing --- substrate/frame/delegated-staking/src/lib.rs | 3 +- substrate/frame/delegated-staking/src/mock.rs | 2 + .../frame/delegated-staking/src/tests.rs | 65 ++++++++++++------- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 04ee1f3525d7..fd44eeb7625e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -636,6 +636,7 @@ impl Pallet { let actual_slash = credit.peek(); // remove the slashed amount + // FIXME(ank4n) add a ledger method to reduce pending slash. delegate.ledger.pending_slash.saturating_reduce(actual_slash); delegate.ledger.total_delegated.saturating_reduce(actual_slash); delegate.save(); @@ -691,8 +692,6 @@ impl Pallet { "delegate should be bonded and not validator" ); - println!("stakeable_balance: {:?}", ledger.stakeable_balance()); - println!("stake: {:?}", T::CoreStaking::stake(&delegate).unwrap()); ensure!( ledger.stakeable_balance() >= T::CoreStaking::total_stake(&delegate) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 8c5113a5e6b0..f31035b3c725 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -266,6 +266,8 @@ impl ExtBuilder { let mut ext = self.build(); ext.execute_with(test); ext.execute_with(|| { + // make sure pool state is correct. + Pools::do_try_state(255).unwrap(); DelegatedStaking::do_try_state().unwrap(); }); } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 5a73af747cb1..c83b2c3f750b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -959,10 +959,10 @@ mod pool_integration { assert_eq!( pool_events_since_last_call(), vec![ - // 301 did not get slashed as all as it unbonded in an era before slash. - // 302 got slashed 50% of 100 = 50. + // 300 did not get slashed as all as it unbonded in an era before slash. + // 301 got slashed 50% of 100 = 50. PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 6, balance: 50 }, - // 303 got slashed 50% of 100 = 50. + // 302 got slashed 50% of 100 = 50. PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 7, balance: 50 }, // Rest of the pool slashed 50% of 800 = 400. PoolsEvent::PoolSlashed { pool_id: 1, balance: 400 }, @@ -990,31 +990,50 @@ mod pool_integration { assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(300).into(), 300, 1)); assert_eq!( events_since_last_call(), - vec![Event::Withdrawn { delegate: pool_acc, delegator: 300, amount: 100 },] + vec![Event::Withdrawn { delegate: pool_acc, delegator: 300, amount: 100 }] ); assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); - let pre_301 = Balances::free_balance(301); - // 301 is slashed and should only be able to withdraw 50. - assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 1)); - assert_eq!( - events_since_last_call(), - vec![ - // half amount is slashed first. - Event::Slashed { delegate: pool_acc, delegator: 301, amount: 50 }, - // rest of it is withdrawn. - Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 }, - ] - ); - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 450); - assert_eq!(held_balance(&301), 0); - assert_eq!(Balances::free_balance(301) - pre_301, 50); + // withdraw the other two delegators (301 and 302) who were unbonding. + for i in 301..=302 { + let pre_balance = Balances::free_balance(i); + let pre_pending_slash = get_pool_delegate(pool_id).ledger.pending_slash; + assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(i).into(), i, 0)); + assert_eq!( + events_since_last_call(), + vec![ + Event::Slashed { delegate: pool_acc, delegator: i, amount: 50 }, + Event::Withdrawn { delegate: pool_acc, delegator: i, amount: 50 }, + ] + ); + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, pre_pending_slash - 50); + assert_eq!(held_balance(&i), 0); + assert_eq!(Balances::free_balance(i) - pre_balance, 50); + } + + // let's update all the slash + let slash_reporter = 99; + // give our reporter some balance. + fund(&slash_reporter, 100); - // delegators cannot unbond without applying pending slash on their accounts. - // slash_amount = total balance held - (balance from active pool + balance from - // unbonding pool) + for i in 303..306 { + let pre_pending_slash = get_pool_delegate(pool_id).ledger.pending_slash; + assert_ok!(Pools::apply_slash(RawOrigin::Signed(slash_reporter).into(), i)); - // when unbonding, should apply slash. Make sure numbers are good. + // each member is slashed 50% of 100 = 50. + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, pre_pending_slash - 50); + // left with 50. + assert_eq!(held_balance(&i), 50); + } + // reporter is paid SlashRewardFraction of the slash, i.e. 10% of 50 = 5 + assert_eq!(Balances::free_balance(slash_reporter), 100 + 5 * 3); + // slash creator + assert_ok!(Pools::apply_slash(RawOrigin::Signed(slash_reporter).into(), creator)); + // all slash should be applied now. + assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 0); + // for creator, 50% of stake should be slashed (250), 10% of which should go to reporter + // (25). + assert_eq!(Balances::free_balance(slash_reporter), 115 + 25); }); } From afc08216baa6408500476a1f74010b39c739384b Mon Sep 17 00:00:00 2001 From: Ankan Date: Sat, 24 Feb 2024 21:23:23 +0100 Subject: [PATCH 147/202] few more fixmes --- substrate/frame/delegated-staking/src/tests.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index c83b2c3f750b..0e34dc2f30ca 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -199,7 +199,11 @@ fn delegate_restrictions() { #[test] fn apply_pending_slash() { - ExtBuilder::default().build_and_execute(|| ()); + ExtBuilder::default().build_and_execute(|| { + ( + // fixme(ank4n): add tests for apply_pending_slash + ) + }); } /// Integration tests with pallet-staking. @@ -488,8 +492,7 @@ mod staking_integration { ExtBuilder::default().build_and_execute(|| { setup_delegation_stake(200, 201, (210..250).collect(), 100, 0); start_era(1); - - // delegate is slashed + // fixme(ank4n): add tests for slashing }); } From fb1eba4441634d2e391c7930727b9413da3a63ce Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 00:05:14 +0100 Subject: [PATCH 148/202] add crate docs --- substrate/frame/delegated-staking/src/lib.rs | 145 +++++++++++++++---- 1 file changed, 120 insertions(+), 25 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index fd44eeb7625e..870fd0274602 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -15,35 +15,129 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg_attr(not(feature = "std"), no_std)] -#![deny(rustdoc::broken_intra_doc_links)] -// FIXME(ank4n): fix docs -//! An implementation of a delegation system for staking that can be utilised using -//! [`DelegationInterface`]. In future, if exposed via extrinsic, these primitives could also be -//! used by off-chain entities, or by foreign multi-locations (via xcm). +//! # Delegated Staking Pallet +//! +//! An abstraction over staking pallet to support delegation of funds to a `delegate` account which +//! can use all the delegated funds to it in the staking pallet as if its own fund. +//! +//! NOTE: The pallet exposes extrinsics which are not yet meant to be exposed in the runtime but +//! only to be used by other pallets in the same runtime. +//! +//! ## Goals +//! +//! The direct nominators on Staking pallet does not scale well. NominationPool was created to +//! address this by pooling delegator funds into one account and then staking it. This though had +//! a very important limitation that the funds were moved from delegator account to pool account +//! and hence the delegator lost control over their funds for using it for other purposes such as +//! governance. This pallet aims to solve this by extending the staking pallet to support a new +//! primitive function: delegation of funds to an account for the intent of staking. +//! +//! #### Reward and Slashing +//! This pallet does not enforce any specific strategy for how rewards or slashes are applied. It +//! is upto the `delegate` account to decide how to apply the rewards and slashes. +//! +//! This importantly allows clients of this pallet to build their own strategies for reward/slashes. +//! For example, a `delegate` account can choose to first slash the reward pot before slashing the +//! delegators. Or part of the reward can go to a insurance fund that can be used to cover any +//! potential future slashes. The goal is to eventually allow foreign MultiLocations +//! (smart contracts or pallets on another chain) to build their own pooled staking solutions +//! similar to `NominationPool`. +//! +//! ## Key Terminologies +//! - *Delegate*: An account who accepts delegations from other accounts (called `Delegators`). +//! - *Delegator*: An account who delegates their funds to a `delegate`. +//! - *DelegationLedger*: A data structure that stores important information about the `delegate` +//! such as their total delegated stake. +//! - *Delegation*: A data structure that stores the amount of funds delegated to a `delegate` by a +//! `delegator`. +//! +//! ## Interface +//! +//! ### Dispatchable Calls +//! The pallet exposes the following [`Call`]s: +//! - `register_as_delegate`: Register an account to be a `Delegate`. Once an account is registered +//! as a `Delegate`, for staking operations, only its delegated funds are used. This means it +//! cannot use its own free balance to stake. +//! - `migrate_to_delegate`: This allows a `Nominator` account to become a `Delegate` account. +//! Explained in more detail in the `Migration` section. +//! - `release`: Release funds to `delegator` from `unclaimed_withdrawals` register of the +//! `delegate`. +//! - `migrate_delegation`: Migrate delegated funds from one account to another. This is useful for +//! example, delegators to a pool account which has migrated to be `delegate` to migrate their +//! funds from pool account back to their own account and delegated to pool as a `delegator`. Once +//! the funds are migrated, the `delegator` can use the funds for other purposes which allows +//! usage of held funds in an account, such as governance. +//! - `delegate_funds`: Delegate funds to a `Delegate` account and update the bond to staking. +//! +//! ### (Staking Interface)[StakingInterface] +//! This pallet reimplements the staking interface as a wrapper implementation over +//! [Config::CoreStaking] to provide delegation based staking. NominationPool can use this pallet as +//! its Staking provider to support delegation based staking from pool accounts. +//! +//! ### (Staking Delegation Support)[StakingDelegationSupport] +//! The pallet implements the staking delegation support trait which staking pallet can use to +//! provide compatibility with this pallet. //! -//! Delegate: Someone who accepts delegations. An account can set their intention to accept -//! delegations by calling [`DelegationInterface::accept_delegations`]. This account cannot have -//! another role in the staking system and once set as `delegate`, can only stake with their -//! delegated balance, i.e. cannot use their own free balance to stake. They can also block new -//! delegations by calling [`DelegationInterface::block_delegations`] or remove themselves from -//! being a `delegate` by calling [`DelegationInterface::kill_delegate`] once all delegations to it -//! are removed. +//! ### (Pool Adapter)[delegation::PoolAdapter] +//! The pallet also implements the pool adapter trait which allows NominationPool to use this pallet +//! to support delegation based staking from pool accounts. This strategy also allows the pool to +//! switch implementations while having minimal changes to its own logic. //! -//! Delegate is also responsible for managing reward distribution and slashes of delegators. +//! ## Lazy Slashing +//! One of the reasons why direct nominators on staking pallet cannot scale well is because all +//! nominators are slashed at the same time. This is expensive and needs to be bounded operation. //! -//! Delegator: Someone who delegates their funds to a `delegate`. A delegator can delegate their -//! funds to one and only one `delegate`. They also can not be a nominator or validator. +//! This pallet implements a lazy slashing mechanism. Any slashes to a `delegate` are posted in its +//! [`DelegationLedger`] as a pending slash. Since the actual amount is held in the multiple +//! `delegator` accounts, this pallet has no way to know how to apply slash. It is `delegate`'s +//! responsibility to apply slashes for each delegator, one at a time. Staking pallet ensures the +//! pending slash never exceeds staked amount and would freeze further withdraws until pending +//! slashes are applied. //! -//! Reward payouts destination: Rewards cannot be paid out to `delegate` account since these funds -//! are not directly exposed. This implies, rewards cannot be auto-compounded and needs to be staked -//! again after distributing it to delegators. +//! `NominationPool` can apply slash for all its members by calling [PoolAdapter::apply_slash]. //! -//! Any slashes to a `delegate` are posted in its [`DelegationLedger`] as a pending slash. Since the -//! actual amount is held in the multiple `delegator` accounts, this pallet has no way to know how -//! to apply slash. It is `delegate`'s responsibility to apply slashes for each delegator, one at a -//! time. Staking pallet ensures the pending slash never exceeds staked amount and would freeze -//! further withdraws until pending slashes are applied. +//! ## Migration from Nominator to Delegate +//! More details here. https://hackmd.io/1jhFRj2MTzeEmAkJgbsBtA?both +//! +//! ## Reward Destination Restrictions +//! This pallets set an important restriction of rewards account to be separate from `delegate` +//! account. This is because, `delegate` balance is not what is directly exposed but the funds that +//! are delegated to it. For `delegate` accounts, we have also no way to auto-compound rewards. The +//! rewards need to be paid out to delegators and then delegated again to the `delegate` account. +//! +//! ## Nomination Pool vs Delegation Staking +//! This pallet is not a replacement for Nomination Pool but adds a new primitive over staking +//! pallet that can be used by Nomination Pool to support delegation based staking. It can be +//! thought of as something in middle of Nomination Pool and Staking Pallet. Technically, these +//! changes could be made in one of those pallets as well but that would have meant significant +//! refactoring and high chances of introducing a regression. With this approach, we can keep the +//! existing pallets with minimal changes and introduce a new pallet that can be optionally used by +//! Nomination Pool. This is completely configurable and a runtime can choose whether to use +//! this pallet or not. +//! +//! With that said, following is the main difference between +//! #### Nomination Pool without delegation support +//! 1) transfer fund from delegator to pool account, and +//! 2) stake from pool account as a direct nominator. +//! +//! #### Nomination Pool with delegation support +//! 1) delegate fund from delegator to pool account, and +//! 2) stake from pool account as a `Delegate` account on the staking pallet. +//! +//! The difference being, in the second approach, the delegated funds will be locked in-place in +//! user's account enabling them to participate in use cases that allows use of `held` funds such +//! as participation in governance voting. +//! +//! Nomination pool still does all the heavy lifting around pool administration, reward +//! distribution, lazy slashing and as such, is not meant to be replaced with this pallet. +//! +//! ## Limitations +//! TODO(ank4n): Add limitations. +//! + +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(rustdoc::broken_intra_doc_links)] + #[cfg(test)] mod mock; @@ -83,7 +177,8 @@ use sp_runtime::{ ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, TryRuntimeError, }; use sp_staking::{ - delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, StakingInterface, + delegation, delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, + StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; From a714f72988cc18066160a6f80303d3688eb009e8 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 00:16:39 +0100 Subject: [PATCH 149/202] fix docs --- substrate/frame/delegated-staking/src/impls.rs | 6 +++--- substrate/frame/delegated-staking/src/lib.rs | 18 +++++++++--------- substrate/frame/delegated-staking/src/tests.rs | 2 +- substrate/primitives/staking/src/delegation.rs | 2 -- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index c49e3ff90ea4..fa4160ca4b38 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -125,7 +125,7 @@ impl StakingInterface for Pallet { /// Withdraw unbonding funds until current era. /// - /// Funds are moved to unclaimed_withdrawals register of the [`DelegationLedger`]. + /// Funds are moved to unclaimed_withdrawals register of the `DelegationLedger`. fn withdraw_unbonded( pool_acc: Self::AccountId, num_slashing_spans: u32, @@ -260,8 +260,8 @@ impl PoolAdapter for Pallet { /// Returns balance of account that is held. /// - /// - For [Delegate] accounts, this is their total delegation amount. - /// - For [Delegators], this is their delegation amount. + /// - For `delegate` accounts, this is their total delegation amount. + /// - For `delegator` accounts, this is their delegation amount. fn total_balance(who: &Self::AccountId) -> Self::Balance { if Self::is_delegator(who) { return Delegation::::get(who).map(|d| d.amount).unwrap_or(Zero::zero()); diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 870fd0274602..a3b2bd8a3a94 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -53,7 +53,7 @@ //! //! ## Interface //! -//! ### Dispatchable Calls +//! #### Dispatchable Calls //! The pallet exposes the following [`Call`]s: //! - `register_as_delegate`: Register an account to be a `Delegate`. Once an account is registered //! as a `Delegate`, for staking operations, only its delegated funds are used. This means it @@ -69,16 +69,16 @@ //! usage of held funds in an account, such as governance. //! - `delegate_funds`: Delegate funds to a `Delegate` account and update the bond to staking. //! -//! ### (Staking Interface)[StakingInterface] +//! #### [Staking Interface](StakingInterface) //! This pallet reimplements the staking interface as a wrapper implementation over //! [Config::CoreStaking] to provide delegation based staking. NominationPool can use this pallet as //! its Staking provider to support delegation based staking from pool accounts. //! -//! ### (Staking Delegation Support)[StakingDelegationSupport] +//! #### [Staking Delegation Support](StakingDelegationSupport) //! The pallet implements the staking delegation support trait which staking pallet can use to //! provide compatibility with this pallet. //! -//! ### (Pool Adapter)[delegation::PoolAdapter] +//! #### [Pool Adapter](delegation::PoolAdapter) //! The pallet also implements the pool adapter trait which allows NominationPool to use this pallet //! to support delegation based staking from pool accounts. This strategy also allows the pool to //! switch implementations while having minimal changes to its own logic. @@ -88,16 +88,17 @@ //! nominators are slashed at the same time. This is expensive and needs to be bounded operation. //! //! This pallet implements a lazy slashing mechanism. Any slashes to a `delegate` are posted in its -//! [`DelegationLedger`] as a pending slash. Since the actual amount is held in the multiple +//! `DelegationLedger` as a pending slash. Since the actual amount is held in the multiple //! `delegator` accounts, this pallet has no way to know how to apply slash. It is `delegate`'s //! responsibility to apply slashes for each delegator, one at a time. Staking pallet ensures the //! pending slash never exceeds staked amount and would freeze further withdraws until pending //! slashes are applied. //! -//! `NominationPool` can apply slash for all its members by calling [PoolAdapter::apply_slash]. +//! `NominationPool` can apply slash for all its members by calling +//! [delegation::PoolAdapter::delegator_slash]. //! //! ## Migration from Nominator to Delegate -//! More details here. https://hackmd.io/1jhFRj2MTzeEmAkJgbsBtA?both +//! More details [here](https://hackmd.io/@ak0n/np-delegated-staking-migration). //! //! ## Reward Destination Restrictions //! This pallets set an important restriction of rewards account to be separate from `delegate` @@ -133,7 +134,6 @@ //! //! ## Limitations //! TODO(ank4n): Add limitations. -//! #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] @@ -388,7 +388,7 @@ pub mod pallet { Self::do_migrate_delegation(&proxy_delegator, &delegator, amount) } - /// Delegate funds to a `Delegate` account and bonds it to [T::CoreStaking]. + /// Delegate funds to a `Delegate` account and bonds it to [Config::CoreStaking]. /// /// If delegation already exists, it increases the delegation by `amount`. #[pallet::call_index(4)] diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 0e34dc2f30ca..9e3bd86ed6b0 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -492,7 +492,7 @@ mod staking_integration { ExtBuilder::default().build_and_execute(|| { setup_delegation_stake(200, 201, (210..250).collect(), 100, 0); start_era(1); - // fixme(ank4n): add tests for slashing + // fixme(ank4n): add tests for slashing }); } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index eff5a72c1889..d686fd8634bb 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -90,8 +90,6 @@ pub trait PoolAdapter { ) -> DispatchResult; /// Revoke delegation to pool account. - /// - /// Similar to [Mutate::transfer] for Direct Stake but in reverse direction to [Self::delegate]. fn release_delegation( who: &Self::AccountId, pool_account: &Self::AccountId, From f337a9d754e8a6521921d6eec4121445bcd5f227 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 09:19:00 +0100 Subject: [PATCH 150/202] limitations --- substrate/frame/delegated-staking/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index a3b2bd8a3a94..e7944e107cc1 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -133,7 +133,10 @@ //! distribution, lazy slashing and as such, is not meant to be replaced with this pallet. //! //! ## Limitations -//! TODO(ank4n): Add limitations. +//! - Rewards are not auto-compounded. +//! - Slashes are lazy and hence there could be a period of time when an account can use funds for +//! operations such as voting in governance even though they should be slashed. +//! #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] From bccb01e46ebae7073e74e4a16ddb12eecdc011a0 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 10:31:52 +0100 Subject: [PATCH 151/202] small edit --- substrate/frame/delegated-staking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index e7944e107cc1..4c0043db8642 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -133,7 +133,7 @@ //! distribution, lazy slashing and as such, is not meant to be replaced with this pallet. //! //! ## Limitations -//! - Rewards are not auto-compounded. +//! - Rewards can not be auto-compounded. //! - Slashes are lazy and hence there could be a period of time when an account can use funds for //! operations such as voting in governance even though they should be slashed. //! From 72ee3e4bc297931271f657230896eb4e7ddd7d58 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 13:09:11 +0100 Subject: [PATCH 152/202] minor doc fixes --- substrate/frame/delegated-staking/src/lib.rs | 66 ++++++++++--------- .../frame/delegated-staking/src/tests.rs | 10 +-- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 4c0043db8642..d095bec22056 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -136,14 +136,12 @@ //! - Rewards can not be auto-compounded. //! - Slashes are lazy and hence there could be a period of time when an account can use funds for //! operations such as voting in governance even though they should be slashed. -//! #![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links)] #[cfg(test)] mod mock; - #[cfg(test)] mod tests; @@ -153,7 +151,6 @@ mod types; use types::*; -// implementation of public traits. mod impls; #[cfg(feature = "runtime-benchmarks")] @@ -206,12 +203,14 @@ pub mod pallet { #[pallet::constant] type PalletId: Get; + /// Currency type. type Currency: FunHoldMutate + FunMutate + FunHoldBalanced; /// Handler for the unbalanced reduction when slashing a delegator. type OnSlash: OnUnbalanced>; + /// Overarching hold reason. type RuntimeHoldReason: From; @@ -230,9 +229,8 @@ pub mod pallet { /// Delegation conditions are not met. /// /// Possible issues are - /// 1) Account does not accept or has blocked delegation. - /// 2) Cannot delegate to self, - /// 3) Cannot delegate to multiple delegates, + /// 1) Cannot delegate to self, + /// 2) Cannot delegate to multiple delegates, InvalidDelegation, /// The account does not have enough funds to perform the operation. NotEnoughFunds, @@ -244,9 +242,9 @@ pub mod pallet { BadState, /// Unapplied pending slash restricts operation on `delegate`. UnappliedSlash, - /// Failed to withdraw amount from Core Staking Ledger. + /// Failed to withdraw amount from Core Staking. WithdrawFailed, - /// This operation is not supported with Delegation Staking. + /// Operation not supported by this pallet. NotSupported, /// Account does not accept delegations. NotAcceptingDelegations, @@ -260,32 +258,26 @@ pub mod pallet { Delegating, } - // #[pallet::genesis_config] - // #[derive(frame_support::DefaultNoBound)] - // pub struct GenesisConfig {} - // - // #[pallet::genesis_build] - // impl BuildGenesisConfig for GenesisConfig { - // fn build(&self) {} - // } - #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { + /// Funds delegated by a delegator. Delegated { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, - Withdrawn { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + /// Funds released to a delegator. + Released { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + /// Funds slashed from a delegator. Slashed { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, } - /// Map of Delegators to their delegation, i.e. (delegate, delegation_amount). + /// Map of Delegators to their `Delegation`. /// - /// Note: We are not using a double map with delegator and `delegate` account as keys since we - /// want to restrict delegators to delegate only to one account. + /// Implementation note: We are not using a double map with `delegator` and `delegate` account + /// as keys since we want to restrict delegators to delegate only to one account at a time. #[pallet::storage] pub(crate) type Delegators = CountedStorageMap<_, Twox64Concat, T::AccountId, Delegation, OptionQuery>; - /// Map of `Delegate` to their Ledger. + /// Map of `Delegate` to their `DelegationLedger`. #[pallet::storage] pub(crate) type Delegates = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; @@ -322,7 +314,17 @@ pub mod pallet { /// Migrate from a `Nominator` account to `Delegate` account. /// - /// Internally transfers minimum balance to a proxy delegator account created for it. + /// The origin needs to + /// - be a `Nominator` with `CoreStaking`, + /// - not already a `Delegate`, + /// - have enough funds to transfer existential deposit to a delegator account created for + /// the migration. + /// + /// This operation will create a new delegator account for the origin called + /// `proxy_delegator` and transfer the staked amount to it. The `proxy_delegator` delegates + /// the funds to the origin making origin a `Delegate` account. The actual `delegator` + /// accounts of the origin can later migrate their funds using [Call::migrate_delegation] to + /// claim back their share of delegated funds from `proxy_delegator` to self. #[pallet::call_index(1)] #[pallet::weight(Weight::default())] pub fn migrate_to_delegate( @@ -330,9 +332,10 @@ pub mod pallet { reward_account: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; - // Ensure is not already a delegate. + // ensure who is not already a delegate. ensure!(!Self::is_delegate(&who), Error::::NotAllowed); + // and they should already be a nominator in `CoreStaking`. ensure!(Self::is_direct_nominator(&who), Error::::NotAllowed); // Reward account cannot be same as `delegate` account. @@ -343,9 +346,10 @@ pub mod pallet { /// Release delegated amount to delegator. /// - /// Tries to withdraw unbonded fund if needed from staking and release amount to delegator. + /// This can be called by existing `delegate` accounts. /// - /// Only `delegate` account can call this. + /// Tries to withdraw unbonded fund from `CoreStaking` if needed and release amount to + /// `delegator`. #[pallet::call_index(2)] #[pallet::weight(Weight::default())] pub fn release( @@ -360,9 +364,9 @@ pub mod pallet { /// Migrate delegated fund. /// - /// This moves delegator funds from `pxoxy_delegator` account to `delegator` account. + /// This can be called by migrating `delegate` accounts. /// - /// Only `delegate` account can call this. + /// This moves delegator funds from `pxoxy_delegator` account to `delegator` account. #[pallet::call_index(3)] #[pallet::weight(Weight::default())] pub fn migrate_delegation( @@ -383,7 +387,7 @@ pub mod pallet { // ensure delegate is sane. ensure!(Self::is_delegate(&delegate), Error::::NotDelegate); - // and has some delegated balance to migrate. + // and has enough delegated balance to migrate. let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, delegate); let balance_remaining = Self::held_balance_of(&proxy_delegator); ensure!(balance_remaining >= amount, Error::::NotEnoughFunds); @@ -420,7 +424,9 @@ pub mod pallet { T::Currency::reducible_balance(&who, Preservation::Preserve, Fortitude::Polite); ensure!(delegator_balance >= amount, Error::::NotEnoughFunds); + // add to delegation Self::do_delegate(&who, &delegate, amount)?; + // bond the amount to `CoreStaking`. Self::do_bond(&delegate, amount) } @@ -622,7 +628,7 @@ impl Pallet { defensive_assert!(released == amount, "hold should have been released fully"); - Self::deposit_event(Event::::Withdrawn { + Self::deposit_event(Event::::Released { delegate: who.clone(), delegator: delegator.clone(), amount, diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 9e3bd86ed6b0..f536584c8e94 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -769,7 +769,7 @@ mod pool_integration { assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 0)); assert_eq!( events_since_last_call(), - vec![Event::Withdrawn { delegate: pool_acc, delegator: 301, amount: 50 }] + vec![Event::Released { delegate: pool_acc, delegator: 301, amount: 50 }] ); assert_eq!( pool_events_since_last_call(), @@ -786,8 +786,8 @@ mod pool_integration { assert_eq!( events_since_last_call(), vec![ - Event::Withdrawn { delegate: pool_acc, delegator: 302, amount: 100 }, - Event::Withdrawn { delegate: pool_acc, delegator: 303, amount: 200 }, + Event::Released { delegate: pool_acc, delegator: 302, amount: 100 }, + Event::Released { delegate: pool_acc, delegator: 303, amount: 200 }, ] ); assert_eq!( @@ -993,7 +993,7 @@ mod pool_integration { assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(300).into(), 300, 1)); assert_eq!( events_since_last_call(), - vec![Event::Withdrawn { delegate: pool_acc, delegator: 300, amount: 100 }] + vec![Event::Released { delegate: pool_acc, delegator: 300, amount: 100 }] ); assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); @@ -1006,7 +1006,7 @@ mod pool_integration { events_since_last_call(), vec![ Event::Slashed { delegate: pool_acc, delegator: i, amount: 50 }, - Event::Withdrawn { delegate: pool_acc, delegator: i, amount: 50 }, + Event::Released { delegate: pool_acc, delegator: i, amount: 50 }, ] ); assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, pre_pending_slash - 50); From 278225ebfd0e56878d31511a32847cb6788c3033 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 13:16:25 +0100 Subject: [PATCH 153/202] rename block delegation --- substrate/frame/delegated-staking/src/lib.rs | 10 ++++++---- substrate/frame/delegated-staking/src/tests.rs | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d095bec22056..cb0b5ea7acb9 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -430,16 +430,18 @@ pub mod pallet { Self::do_bond(&delegate, amount) } - /// Stop accepting new delegation. + /// Toggle delegate status to start or stop accepting new delegations. /// - /// To unblock, pass false. + /// This can only be used by existing delegates. If not a delegate yet, use + /// [Call::register_as_delegate] first. #[pallet::call_index(5)] #[pallet::weight(Weight::default())] - pub fn block_delegations(origin: OriginFor, block: bool) -> DispatchResult { + pub fn toggle_delegate_status(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let delegate = Delegate::::from(&who)?; - delegate.update_status(block).save(); + let should_block = !delegate.ledger.blocked; + delegate.update_status(should_block).save(); Ok(()) } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index f536584c8e94..b253fad01871 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -462,7 +462,7 @@ mod staking_integration { } #[test] - fn block_delegations() { + fn toggle_delegate_status() { ExtBuilder::default().build_and_execute(|| { assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201)); @@ -471,7 +471,7 @@ mod staking_integration { assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); // delegate blocks delegation - assert_ok!(DelegatedStaking::block_delegations(RawOrigin::Signed(200).into(), true)); + assert_ok!(DelegatedStaking::toggle_delegate_status(RawOrigin::Signed(200).into())); // cannot delegate to it anymore assert_noop!( @@ -480,7 +480,7 @@ mod staking_integration { ); // delegate can unblock delegation - assert_ok!(DelegatedStaking::block_delegations(RawOrigin::Signed(200).into(), false)); + assert_ok!(DelegatedStaking::toggle_delegate_status(RawOrigin::Signed(200).into())); // delegation works again assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); From b32dceda913a6a03d3ae1ccb93f375c788e724f5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 13:23:35 +0100 Subject: [PATCH 154/202] add slash call --- substrate/frame/delegated-staking/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index cb0b5ea7acb9..bdecb3834c11 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -445,6 +445,18 @@ pub mod pallet { Ok(()) } + + /// Apply slash to a delegator account. + /// + /// `Delegate` accounts with pending slash in their ledger can call this to apply slash to + /// one of its `delegator` account. Each slash to a delegator account needs to be posted + /// separately until all pending slash is cleared. + #[pallet::call_index(6)] + #[pallet::weight(Weight::default())] + pub fn apply_slash(origin: OriginFor, delegator: T::AccountId, amount: BalanceOf) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::do_slash(who, delegator, amount, None) + } } #[pallet::hooks] From 03efe85c07122c6e007b965bf3f1e299297a1959 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 13:23:49 +0100 Subject: [PATCH 155/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index bdecb3834c11..4879c4699bbd 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -440,7 +440,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let delegate = Delegate::::from(&who)?; - let should_block = !delegate.ledger.blocked; + let should_block = !delegate.ledger.blocked; delegate.update_status(should_block).save(); Ok(()) @@ -453,7 +453,11 @@ pub mod pallet { /// separately until all pending slash is cleared. #[pallet::call_index(6)] #[pallet::weight(Weight::default())] - pub fn apply_slash(origin: OriginFor, delegator: T::AccountId, amount: BalanceOf) -> DispatchResult { + pub fn apply_slash( + origin: OriginFor, + delegator: T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_slash(who, delegator, amount, None) } From 9807c51befb68ddab932f6150c8b0f9795a06b6b Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 13:40:42 +0100 Subject: [PATCH 156/202] minor refactors --- substrate/frame/delegated-staking/src/lib.rs | 15 +++++++++------ substrate/frame/delegated-staking/src/types.rs | 18 +++++++++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 4879c4699bbd..df60893d53e0 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -475,6 +475,7 @@ pub mod pallet { } impl Pallet { + /// Derive a (keyless) pot account from the given delegate account and account type. pub(crate) fn sub_account( account_type: AccountType, delegate_account: T::AccountId, @@ -486,20 +487,23 @@ impl Pallet { pub(crate) fn held_balance_of(who: &T::AccountId) -> BalanceOf { T::Currency::balance_on_hold(&HoldReason::Delegating.into(), who) } + + /// Returns true if who is registered as a `Delegate`. fn is_delegate(who: &T::AccountId) -> bool { >::contains_key(who) } + /// Returns true if who is delegating to a `Delegate` account. fn is_delegator(who: &T::AccountId) -> bool { >::contains_key(who) } - /// Returns true if who is not already staking. + /// Returns true if who is not already staking on [`Config::CoreStaking`]. fn not_direct_staker(who: &T::AccountId) -> bool { T::CoreStaking::status(&who).is_err() } - /// Returns true if who is not already staking. + /// Returns true if who is a [`StakerStatus::Nominator`] on [`Config::CoreStaking`]. fn is_direct_nominator(who: &T::AccountId) -> bool { T::CoreStaking::status(who) .map(|status| matches!(status, StakerStatus::Nominator(_))) @@ -619,7 +623,7 @@ impl Pallet { ensure!(delegate.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); // book keep into ledger - delegate.try_withdraw(amount)?; + delegate.remove_unclaimed_withdraw(amount)?; // kill delegate if not delegated and nothing to claim anymore. delegate.save_or_kill()?; @@ -678,7 +682,7 @@ impl Pallet { let new_withdrawn = pre_total.checked_sub(&post_total).defensive_ok_or(Error::::BadState)?; - delegate.try_add_unclaimed_withdraw(new_withdrawn)?; + delegate.add_unclaimed_withdraw(new_withdrawn)?; delegate.clone().save(); @@ -759,8 +763,7 @@ impl Pallet { // remove the slashed amount // FIXME(ank4n) add a ledger method to reduce pending slash. - delegate.ledger.pending_slash.saturating_reduce(actual_slash); - delegate.ledger.total_delegated.saturating_reduce(actual_slash); + delegate.remove_slash(actual_slash); delegate.save(); delegation diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 6d68f4af62f2..e1b818aa9c82 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -19,6 +19,7 @@ //! Basic types used in delegated staking. use super::*; +use frame_support::traits::DefensiveSaturating; /// The type of pot account being created. #[derive(Encode, Decode)] @@ -93,7 +94,8 @@ pub struct DelegationLedger { /// Sum of all delegated funds to this `delegate`. #[codec(compact)] pub total_delegated: BalanceOf, - /// Funds that are withdrawn from core staking but not released to delegator/s. + /// Funds that are withdrawn from core staking but not released to delegator/s. It is a subset + /// of `total_delegated` and can never be greater than it. // FIXME(ank4n): Check/test about rebond: where delegator rebond what is unlocking. #[codec(compact)] pub unclaimed_withdrawals: BalanceOf, @@ -158,7 +160,10 @@ impl Delegate { Ok(Delegate { key: delegate.clone(), ledger }) } - pub(crate) fn try_withdraw(&mut self, amount: BalanceOf) -> Result<(), DispatchError> { + pub(crate) fn remove_unclaimed_withdraw( + &mut self, + amount: BalanceOf, + ) -> Result<(), DispatchError> { self.ledger.total_delegated = self .ledger .total_delegated @@ -173,7 +178,7 @@ impl Delegate { Ok(()) } - pub(crate) fn try_add_unclaimed_withdraw( + pub(crate) fn add_unclaimed_withdraw( &mut self, amount: BalanceOf, ) -> Result<(), DispatchError> { @@ -214,6 +219,10 @@ impl Delegate { net_balance.saturating_sub(bonded_stake) } + pub(crate) fn remove_slash(&mut self, amount: BalanceOf) { + self.ledger.pending_slash.defensive_saturating_reduce(amount); + self.ledger.total_delegated.defensive_saturating_reduce(amount); + } pub(crate) fn ledger(&self) -> Option> { DelegationLedger::::get(&self.key) } @@ -239,6 +248,9 @@ impl Delegate { self.ledger.save(&key) } + /// Save self and remove if no delegation left. + /// + /// Can return error if the delegate is in an unexpected state. pub(crate) fn save_or_kill(self) -> Result<(), DispatchError> { let key = self.key; // see if delegate can be killed From dbde2868bd0fc33fab135a930c1e5174ff9b17ac Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:06:28 +0100 Subject: [PATCH 157/202] minor refactors --- .../frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 15 ++++---- substrate/frame/delegated-staking/src/mock.rs | 2 +- .../frame/delegated-staking/src/tests.rs | 16 ++++----- .../frame/delegated-staking/src/types.rs | 36 ++++++++++++------- .../frame/nomination-pools/src/adapter.rs | 2 +- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index fa4160ca4b38..57bb4d601290 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -254,7 +254,7 @@ impl PoolAdapter for Pallet { /// Equivalent to [FunInspect::balance] for non delegate accounts. fn releasable_balance(who: &Self::AccountId) -> Self::Balance { Delegate::::from(who) - .map(|delegate| delegate.unbonded()) + .map(|delegate| delegate.total_unbonded()) .unwrap_or(Zero::zero()) } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index df60893d53e0..f35c29f432b4 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -168,16 +168,15 @@ use frame_support::{ tokens::{fungible::Credit, Fortitude, Precision, Preservation}, Defensive, DefensiveOption, Imbalance, OnUnbalanced, }, - transactional, weights::Weight, }; use sp_runtime::{ traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, - ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, TryRuntimeError, + ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; use sp_staking::{ - delegation, delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, + delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; @@ -695,7 +694,7 @@ impl Pallet { destination_delegator: &T::AccountId, amount: BalanceOf, ) -> DispatchResult { - let mut source_delegation = + let source_delegation = Delegators::::get(&source_delegator).defensive_ok_or(Error::::BadState)?; // some checks that must have already been checked before. @@ -749,7 +748,7 @@ impl Pallet { maybe_reporter: Option, ) -> DispatchResult { let mut delegate = Delegate::::from(&delegate_acc)?; - let mut delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; + let delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; ensure!(delegation.delegate == delegate_acc, Error::::NotDelegate); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); @@ -794,7 +793,7 @@ use sp_std::collections::btree_map::BTreeMap; #[cfg(any(test, feature = "try-runtime"))] impl Pallet { - pub(crate) fn do_try_state() -> Result<(), TryRuntimeError> { + pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { // build map to avoid reading storage multiple times. let delegation_map = Delegators::::iter().collect::>(); let ledger_map = Delegates::::iter().collect::>(); @@ -807,7 +806,7 @@ impl Pallet { fn check_delegates( ledgers: BTreeMap>, - ) -> Result<(), TryRuntimeError> { + ) -> Result<(), sp_runtime::TryRuntimeError> { for (delegate, ledger) in ledgers { ensure!( matches!( @@ -831,7 +830,7 @@ impl Pallet { fn check_delegators( delegations: BTreeMap>, ledger: BTreeMap>, - ) -> Result<(), TryRuntimeError> { + ) -> Result<(), sp_runtime::TryRuntimeError> { let mut delegation_aggregation = BTreeMap::>::new(); for (delegator, delegation) in delegations.iter() { ensure!( diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index f31035b3c725..439dc23df6e4 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -17,7 +17,7 @@ use crate::{self as delegated_staking, types::Delegate, HoldReason}; use frame_support::{ - assert_noop, assert_ok, derive_impl, + assert_ok, derive_impl, pallet_prelude::*, parameter_types, traits::{fungible::InspectHold, ConstU64, Currency}, diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index b253fad01871..8de4d3f419fe 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -246,7 +246,6 @@ mod staking_integration { let delegate_obj = get_delegate(&delegate); assert_eq!(delegate_obj.ledger.stakeable_balance(), delegated_balance); assert_eq!(delegate_obj.available_to_bond(), 0); - assert_eq!(delegate_obj.available_to_bond(), 0); assert_eq!(delegate_obj.bonded_stake(), delegated_balance); } @@ -607,7 +606,7 @@ mod pool_integration { // verify state assert_eq!(delegate.ledger.effective_balance(), delegate_amount); assert_eq!(delegate.available_to_bond(), 0); - assert_eq!(delegate.unbonded(), 0); + assert_eq!(delegate.total_unbonded(), 0); }); } @@ -640,7 +639,7 @@ mod pool_integration { assert_eq!(pool_delegate.ledger.effective_balance(), staked_amount); assert_eq!(pool_delegate.bonded_stake(), staked_amount); assert_eq!(pool_delegate.available_to_bond(), 0); - assert_eq!(pool_delegate.unbonded(), 0); + assert_eq!(pool_delegate.total_unbonded(), 0); // let a bunch of delegators join this pool for i in 301..350 { @@ -650,10 +649,11 @@ mod pool_integration { assert_eq!(held_balance(&i), 100 + i); } - assert_eq!(pool_delegate.refresh().unwrap().ledger.effective_balance(), staked_amount); + let pool_delegate = pool_delegate.refresh().unwrap(); + assert_eq!(pool_delegate.ledger.effective_balance(), staked_amount); assert_eq!(pool_delegate.bonded_stake(), staked_amount); assert_eq!(pool_delegate.available_to_bond(), 0); - assert_eq!(pool_delegate.unbonded(), 0); + assert_eq!(pool_delegate.total_unbonded(), 0); }); } @@ -827,17 +827,17 @@ mod pool_integration { start_era(5); // withdraw pool should withdraw 1000 tokens assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000); + assert_eq!(get_pool_delegate(pool_id).total_unbonded(), 1000); start_era(6); // should withdraw 500 more assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).unbonded(), 1000 + 500); + assert_eq!(get_pool_delegate(pool_id).total_unbonded(), 1000 + 500); start_era(7); // Nothing to withdraw, still at 1500. assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).unbonded(), 1500); + assert_eq!(get_pool_delegate(pool_id).total_unbonded(), 1500); }); } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index e1b818aa9c82..847c3a7cf78a 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -30,6 +30,7 @@ pub(crate) enum AccountType { ProxyDelegator, } +/// Information about delegation of a `delegator`. #[derive(Default, Encode, Clone, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct Delegation { @@ -64,6 +65,7 @@ impl Delegation { } /// Checked increase of delegation amount. Consumes self and returns a new copy. + #[allow(unused)] pub(crate) fn increase_delegation(self, amount: BalanceOf) -> Option { let updated_delegation = self.amount.checked_add(&amount)?; Some(Delegation::from(&self.delegate, updated_delegation)) @@ -148,6 +150,7 @@ impl DelegationLedger { } } +/// Wrapper around `DelegationLedger` to provide additional functionality. #[derive(Clone)] pub struct Delegate { pub key: T::AccountId, @@ -160,6 +163,10 @@ impl Delegate { Ok(Delegate { key: delegate.clone(), ledger }) } + /// Remove funds that are withdrawn from [Config::CoreStaking] but not claimed by a delegator. + /// + /// Checked decrease of delegation amount from `total_delegated` and `unclaimed_withdrawals` + /// registers. Mutates self. pub(crate) fn remove_unclaimed_withdraw( &mut self, amount: BalanceOf, @@ -178,6 +185,7 @@ impl Delegate { Ok(()) } + /// Add funds that are withdrawn from [Config::CoreStaking] to be claimed by delegators later. pub(crate) fn add_unclaimed_withdraw( &mut self, amount: BalanceOf, @@ -190,42 +198,44 @@ impl Delegate { Ok(()) } - // re-reads the delegate from database and returns a new instance. + + /// Reloads self from storage. + #[allow(unused)] pub(crate) fn refresh(&self) -> Result, DispatchError> { Self::from(&self.key) } + /// Amount that is delegated but not bonded yet. + /// + /// This importantly does not include `unclaimed_withdrawals` as those should not be bonded + /// again unless explicitly requested. pub(crate) fn available_to_bond(&self) -> BalanceOf { let bonded_stake = self.bonded_stake(); - - let stakeable = - self.ledger().map(|ledger| ledger.stakeable_balance()).unwrap_or(Zero::zero()); + let stakeable = self.ledger.stakeable_balance(); defensive_assert!(stakeable >= bonded_stake, "cannot expose more than delegate balance"); stakeable.saturating_sub(bonded_stake) } - /// Similar to [`Self::available_to_bond`] but includes `DelegationLedger.unclaimed_withdrawals` - /// as well. - pub(crate) fn unbonded(&self) -> BalanceOf { + /// Balance of `Delegate` that is not bonded. + /// + /// Includes `unclaimed_withdrawals` of `Delegate`. + pub(crate) fn total_unbonded(&self) -> BalanceOf { let bonded_stake = self.bonded_stake(); - let net_balance = - self.ledger().map(|ledger| ledger.effective_balance()).unwrap_or(Zero::zero()); + let net_balance = self.ledger.effective_balance(); defensive_assert!(net_balance >= bonded_stake, "cannot expose more than delegate balance"); net_balance.saturating_sub(bonded_stake) } + /// Remove slashes that are applied. pub(crate) fn remove_slash(&mut self, amount: BalanceOf) { self.ledger.pending_slash.defensive_saturating_reduce(amount); self.ledger.total_delegated.defensive_saturating_reduce(amount); } - pub(crate) fn ledger(&self) -> Option> { - DelegationLedger::::get(&self.key) - } pub(crate) fn bonded_stake(&self) -> BalanceOf { T::CoreStaking::total_stake(&self.key).unwrap_or(Zero::zero()) @@ -250,7 +260,7 @@ impl Delegate { /// Save self and remove if no delegation left. /// - /// Can return error if the delegate is in an unexpected state. + /// Returns error if the delegate is in an unexpected state. pub(crate) fn save_or_kill(self) -> Result<(), DispatchError> { let key = self.key; // see if delegate can be killed diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 0fbb5826f145..b6c5f12d2773 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -69,7 +69,7 @@ impl PoolAdapter for NoDelegation { Ok(()) } - fn has_pending_slash(delegate: &Self::AccountId) -> bool { + fn has_pending_slash(_delegate: &Self::AccountId) -> bool { // for direct staking, slashing is eager, and we don't need to do anything here. false } From 0ccd901867ba4bf474ceab2ed68fc476431104d0 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:06:47 +0100 Subject: [PATCH 158/202] fmt --- substrate/frame/delegated-staking/src/lib.rs | 3 +-- substrate/frame/delegated-staking/src/types.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index f35c29f432b4..476000bf16bb 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -176,8 +176,7 @@ use sp_runtime::{ ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; use sp_staking::{ - delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, - StakingInterface, + delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 847c3a7cf78a..8f8b523ff144 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -231,7 +231,7 @@ impl Delegate { net_balance.saturating_sub(bonded_stake) } - /// Remove slashes that are applied. + /// Remove slashes that are applied. pub(crate) fn remove_slash(&mut self, amount: BalanceOf) { self.ledger.pending_slash.defensive_saturating_reduce(amount); self.ledger.total_delegated.defensive_saturating_reduce(amount); From 814d2a51daca823bb0f04cebec0d23b9b3f1e3e7 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:10:06 +0100 Subject: [PATCH 159/202] refactor types --- substrate/frame/delegated-staking/src/lib.rs | 7 ++----- substrate/frame/delegated-staking/src/types.rs | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 476000bf16bb..13dc19a4592f 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -620,11 +620,8 @@ impl Pallet { // if we still do not have enough funds to release, abort. ensure!(delegate.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); - // book keep into ledger - delegate.remove_unclaimed_withdraw(amount)?; - - // kill delegate if not delegated and nothing to claim anymore. - delegate.save_or_kill()?; + // claim withdraw from delegate. + delegate.remove_unclaimed_withdraw(amount)?.save_or_kill()?; // book keep delegation delegation.amount = delegation diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 8f8b523ff144..d232258f9682 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -168,21 +168,28 @@ impl Delegate { /// Checked decrease of delegation amount from `total_delegated` and `unclaimed_withdrawals` /// registers. Mutates self. pub(crate) fn remove_unclaimed_withdraw( - &mut self, + self, amount: BalanceOf, - ) -> Result<(), DispatchError> { - self.ledger.total_delegated = self + ) -> Result { + let new_total_delegated = self .ledger .total_delegated .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - self.ledger.unclaimed_withdrawals = self + let new_unclaimed_withdrawals = self .ledger .unclaimed_withdrawals .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - Ok(()) + Ok(Delegate { + ledger: DelegationLedger { + total_delegated: new_total_delegated, + unclaimed_withdrawals: new_unclaimed_withdrawals, + ..self.ledger + }, + ..self + }) } /// Add funds that are withdrawn from [Config::CoreStaking] to be claimed by delegators later. From 4e681979dc8793725a6db1d26efb4f4816b3a32f Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:17:17 +0100 Subject: [PATCH 160/202] minor refactor --- substrate/frame/delegated-staking/src/lib.rs | 12 ++++----- .../frame/delegated-staking/src/types.rs | 27 +++++++++++++------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 13dc19a4592f..26fe02e9a756 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -658,7 +658,7 @@ impl Pallet { delegate_acc: &T::AccountId, num_slashing_spans: u32, ) -> Result, DispatchError> { - let mut delegate = Delegate::::from(delegate_acc)?; + let delegate = Delegate::::from(delegate_acc)?; let pre_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; let stash_killed: bool = @@ -677,7 +677,7 @@ impl Pallet { let new_withdrawn = pre_total.checked_sub(&post_total).defensive_ok_or(Error::::BadState)?; - delegate.add_unclaimed_withdraw(new_withdrawn)?; + let delegate = delegate.add_unclaimed_withdraw(new_withdrawn)?; delegate.clone().save(); @@ -743,7 +743,7 @@ impl Pallet { amount: BalanceOf, maybe_reporter: Option, ) -> DispatchResult { - let mut delegate = Delegate::::from(&delegate_acc)?; + let delegate = Delegate::::from(&delegate_acc)?; let delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; ensure!(delegation.delegate == delegate_acc, Error::::NotDelegate); @@ -756,10 +756,8 @@ impl Pallet { let actual_slash = credit.peek(); - // remove the slashed amount - // FIXME(ank4n) add a ledger method to reduce pending slash. - delegate.remove_slash(actual_slash); - delegate.save(); + // remove the applied slashed amount from delegate. + delegate.remove_slash(actual_slash).save(); delegation .decrease_delegation(actual_slash) diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index d232258f9682..30c700df3820 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -166,7 +166,7 @@ impl Delegate { /// Remove funds that are withdrawn from [Config::CoreStaking] but not claimed by a delegator. /// /// Checked decrease of delegation amount from `total_delegated` and `unclaimed_withdrawals` - /// registers. Mutates self. + /// registers. Consumes self and returns a new instance of self if success. pub(crate) fn remove_unclaimed_withdraw( self, amount: BalanceOf, @@ -194,16 +194,22 @@ impl Delegate { /// Add funds that are withdrawn from [Config::CoreStaking] to be claimed by delegators later. pub(crate) fn add_unclaimed_withdraw( - &mut self, + self, amount: BalanceOf, - ) -> Result<(), DispatchError> { - self.ledger.unclaimed_withdrawals = self + ) -> Result { + let new_unclaimed_withdrawals = self .ledger .unclaimed_withdrawals .checked_add(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - Ok(()) + Ok(Delegate { + ledger: DelegationLedger { + unclaimed_withdrawals: new_unclaimed_withdrawals, + ..self.ledger + }, + ..self + }) } /// Reloads self from storage. @@ -239,9 +245,14 @@ impl Delegate { } /// Remove slashes that are applied. - pub(crate) fn remove_slash(&mut self, amount: BalanceOf) { - self.ledger.pending_slash.defensive_saturating_reduce(amount); - self.ledger.total_delegated.defensive_saturating_reduce(amount); + pub(crate) fn remove_slash(self, amount: BalanceOf) -> Self { + let pending_slash = self.ledger.pending_slash.defensive_saturating_sub(amount); + let total_delegated = self.ledger.total_delegated.defensive_saturating_sub(amount); + + Delegate { + ledger: DelegationLedger { pending_slash, total_delegated, ..self.ledger }, + ..self + } } pub(crate) fn bonded_stake(&self) -> BalanceOf { From 8d00bdf18be97d1d63ca1adde7594c6e7f500714 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:25:56 +0100 Subject: [PATCH 161/202] improve np diff --- substrate/frame/nomination-pools/src/lib.rs | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 5c084fb30eb9..911323b3804a 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -748,7 +748,7 @@ impl Commission { // // Throttled if the attempted increase in commission is greater than `max_increase`. if (*to).saturating_sub(commission_as_percent) > t.max_increase { - return true; + return true } // Test for `min_delay` throttling. @@ -771,7 +771,7 @@ impl Commission { blocks_surpassed < t.min_delay } }, - ); + ) } false } @@ -829,7 +829,7 @@ impl Commission { ); if let Some(old) = self.max.as_mut() { if new_max > *old { - return Err(Error::::MaxCommissionRestricted.into()); + return Err(Error::::MaxCommissionRestricted.into()) } *old = new_max; } else { @@ -1215,7 +1215,7 @@ impl BondedPool { }, (false, true) => { // the depositor can simply not be unbonded permissionlessly, period. - return Err(Error::::DoesNotHavePermission.into()); + return Err(Error::::DoesNotHavePermission.into()) }, }; @@ -2991,7 +2991,7 @@ impl Pallet { let balance = T::U256ToBalance::convert; if current_balance.is_zero() || current_points.is_zero() || points.is_zero() { // There is nothing to unbond - return Zero::zero(); + return Zero::zero() } // Equivalent of (current_balance / current_points) * points @@ -3028,7 +3028,7 @@ impl Pallet { // will be zero. let pending_rewards = member.pending_rewards(current_reward_counter)?; if pending_rewards.is_zero() { - return Ok(pending_rewards); + return Ok(pending_rewards) } // IFF the reward is non-zero alter the member and reward pool info. @@ -3256,7 +3256,7 @@ impl Pallet { let min_balance = T::Currency::minimum_balance(); if pre_frozen_balance == min_balance { - return Err(Error::::NothingToAdjust.into()); + return Err(Error::::NothingToAdjust.into()) } // Update frozen amount with current ED. @@ -3363,7 +3363,7 @@ impl Pallet { #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] pub fn do_try_state(level: u8) -> Result<(), TryRuntimeError> { if level.is_zero() { - return Ok(()); + return Ok(()) } // note: while a bit wacky, since they have the same key, even collecting to vec should // result in the same set of keys, in the same order. @@ -3496,7 +3496,7 @@ impl Pallet { ); if level <= 1 { - return Ok(()); + return Ok(()) } for (pool_id, _pool) in BondedPools::::iter() { @@ -3631,9 +3631,7 @@ impl sp_staking::OnStakingUpdate> for Pall slashed_unlocking: &BTreeMap>, total_slashed: BalanceOf, ) { - let Some(pool_id) = ReversePoolIdLookup::::get(pool_account) else { - return; - }; + let Some(pool_id) = ReversePoolIdLookup::::get(pool_account) else { return }; // As the slashed account belongs to a `BondedPool` the `TotalValueLocked` decreases and // an event is emitted. TotalValueLocked::::mutate(|tvl| { From e3602670415ec40bcecea2085b5d3ad29b689318 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:26:55 +0100 Subject: [PATCH 162/202] missed semicolon --- substrate/frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 911323b3804a..ab09fbba15b6 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -742,7 +742,7 @@ impl Commission { // do not throttle if `to` is the same or a decrease in commission. if *to <= commission_as_percent { - return false; + return false } // Test for `max_increase` throttling. // From 05e7ba6b83435cba57e1a9102bd0c79841545940 Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 15:44:17 +0100 Subject: [PATCH 163/202] clean diff --- substrate/frame/staking/src/pallet/impls.rs | 1 + substrate/frame/staking/src/pallet/mod.rs | 1 - substrate/frame/staking/src/slashing.rs | 13 +++++++------ substrate/primitives/staking/src/lib.rs | 9 +++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 08ab7c81afb1..3b2a7b512e2e 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1879,6 +1879,7 @@ impl StakingDelegationSupport for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; fn stakeable_balance(_who: &Self::AccountId) -> Self::Balance { + defensive!("stakeable balance should not have been called for NoDelegation"); BalanceOf::::zero() } fn is_delegate(_who: &Self::AccountId) -> bool { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 80cde75abc21..27fdb320b5ba 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -863,7 +863,6 @@ pub mod pallet { ControllerDeprecated, /// Provided reward destination is not allowed. RewardDestinationRestricted, - // FIXME(ank4n): look at the error again /// Not enough funds available to withdraw NotEnoughFunds, } diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index bed0b9f621d9..00cdf64f5d4a 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -125,7 +125,7 @@ impl SlashingSpans { pub(crate) fn end_span(&mut self, now: EraIndex) -> bool { let next_start = now.defensive_saturating_add(1); if next_start <= self.last_start { - return false; + return false } let last_length = next_start.defensive_saturating_sub(self.last_start); @@ -242,7 +242,7 @@ pub(crate) fn compute_slash( // kick out the validator even if they won't be slashed, // as long as the misbehavior is from their most recent slashing span. kick_out_if_recent::(params); - return None; + return None } let prior_slash_p = ValidatorSlashInEra::::get(¶ms.slash_era, params.stash) @@ -264,7 +264,7 @@ pub(crate) fn compute_slash( // pays out some reward even if the latest report is not max-in-era. // we opt to avoid the nominator lookups and edits and leave more rewards // for more drastic misbehavior. - return None; + return None } // apply slash to validator. @@ -542,7 +542,7 @@ impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> { fn drop(&mut self) { // only update on disk if we slashed this account. if !self.dirty { - return; + return } if let Some((start, end)) = self.spans.prune(self.window_start) { @@ -613,8 +613,9 @@ pub fn do_slash( if value.is_zero() { // nothing to do - return; + return } + if lazy_slash { // If delegated staking, report slash and move on. T::DelegationSupport::report_slash(stash, value); @@ -675,7 +676,7 @@ fn pay_reporters( // nobody to pay out to or nothing to pay; // just treat the whole value as slashed. T::Slash::on_unbalanced(slashed_imbalance); - return; + return } // take rewards out of the slashed imbalance. diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 46825775fd62..99f78234a218 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -98,6 +98,9 @@ pub struct Stake { #[impl_trait_for_tuples::impl_for_tuples(10)] pub trait OnStakingUpdate { /// Fired when the stake amount of someone updates. + /// + /// This is effectively any changes to the bond amount, such as bonding more funds, and + /// unbonding. fn on_stake_update(_who: &AccountId, _prev_stake: Option>) {} /// Fired when someone sets their intention to nominate. @@ -113,6 +116,9 @@ pub trait OnStakingUpdate { fn on_nominator_update(_who: &AccountId, _prev_nominations: Vec) {} /// Fired when someone removes their intention to nominate, either due to chill or validating. + /// + /// The set of nominations at the time of removal is provided as it can no longer be fetched in + /// any way. fn on_nominator_remove(_who: &AccountId, _nominations: Vec) {} /// Fired when someone sets their intention to validate. @@ -151,6 +157,9 @@ pub trait OnStakingUpdate { } /// A generic representation of a staking implementation. +/// +/// This interface uses the terminology of NPoS, but it is aims to be generic enough to cover other +/// implementations as well. pub trait StakingInterface { /// Balance type used by the staking system. type Balance: Sub From 95e3ca0fa5b2814a68c481424399b8984937888d Mon Sep 17 00:00:00 2001 From: Ankan Date: Sun, 25 Feb 2024 20:56:12 +0100 Subject: [PATCH 164/202] doc fix --- substrate/frame/delegated-staking/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 26fe02e9a756..d58696d14b1a 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -78,7 +78,7 @@ //! The pallet implements the staking delegation support trait which staking pallet can use to //! provide compatibility with this pallet. //! -//! #### [Pool Adapter](delegation::PoolAdapter) +//! #### [Pool Adapter](sp_staking::delegation::PoolAdapter) //! The pallet also implements the pool adapter trait which allows NominationPool to use this pallet //! to support delegation based staking from pool accounts. This strategy also allows the pool to //! switch implementations while having minimal changes to its own logic. @@ -95,7 +95,7 @@ //! slashes are applied. //! //! `NominationPool` can apply slash for all its members by calling -//! [delegation::PoolAdapter::delegator_slash]. +//! [PoolAdapter::delegator_slash](sp_staking::delegation::PoolAdapter::delegator_slash). //! //! ## Migration from Nominator to Delegate //! More details [here](https://hackmd.io/@ak0n/np-delegated-staking-migration). From 89dd29be3315d762196f8bb9526784c9131bb27f Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 26 Feb 2024 15:10:45 +0100 Subject: [PATCH 165/202] rename to transferable --- substrate/frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/nomination-pools/src/adapter.rs | 4 +++- substrate/frame/nomination-pools/src/lib.rs | 2 +- substrate/primitives/staking/src/delegation.rs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 57bb4d601290..2009c2e6596a 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -252,7 +252,7 @@ impl PoolAdapter for Pallet { /// Return balance of the `Delegate` (pool account) that is not bonded. /// /// Equivalent to [FunInspect::balance] for non delegate accounts. - fn releasable_balance(who: &Self::AccountId) -> Self::Balance { + fn transferable_balance(who: &Self::AccountId) -> Self::Balance { Delegate::::from(who) .map(|delegate| delegate.total_unbonded()) .unwrap_or(Zero::zero()) diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index b6c5f12d2773..0c41f1ce0fb2 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -23,11 +23,13 @@ use crate::*; /// tokens in delegator's accounts. pub struct NoDelegation(PhantomData); + +/// TODO(ankan) Call it FundManager/CurrencyAdapter/DelegationManager impl PoolAdapter for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; - fn releasable_balance(who: &Self::AccountId) -> Self::Balance { + fn transferable_balance(who: &Self::AccountId) -> Self::Balance { // Note on why we can't use `Currency::reducible_balance`: Since pooled account has a // provider (staking pallet), the account can not be set expendable by // `pallet-nomination-pool`. This means reducible balance always returns balance preserving diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index ab09fbba15b6..e929666f06c5 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1056,7 +1056,7 @@ impl BondedPool { /// The pools balance that is transferable provided it is expendable by staking pallet. fn transferable_balance(&self) -> BalanceOf { - T::PoolAdapter::releasable_balance(&self.bonded_account()) + T::PoolAdapter::transferable_balance(&self.bonded_account()) } fn is_root(&self, who: &T::AccountId) -> bool { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index d686fd8634bb..40e06ab5157e 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -69,7 +69,7 @@ pub trait PoolAdapter { type AccountId: Clone + sp_std::fmt::Debug; /// Balance that is free and can be released to delegator. - fn releasable_balance(who: &Self::AccountId) -> Self::Balance; + fn transferable_balance(who: &Self::AccountId) -> Self::Balance; /// Total balance of the account held for staking. fn total_balance(who: &Self::AccountId) -> Self::Balance; From 8a41c860f6862efa1c31004b03122f09e235cc1b Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 00:09:46 +0100 Subject: [PATCH 166/202] stake adapter --- .../frame/nomination-pools/src/adapter.rs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 0c41f1ce0fb2..adcda1277198 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -23,7 +23,6 @@ use crate::*; /// tokens in delegator's accounts. pub struct NoDelegation(PhantomData); - /// TODO(ankan) Call it FundManager/CurrencyAdapter/DelegationManager impl PoolAdapter for NoDelegation { type Balance = BalanceOf; @@ -87,3 +86,35 @@ impl PoolAdapter for NoDelegation { Err(Error::::NothingToSlash.into()) } } + +/// Stake Strategy trait that can support different ways of staking such as `Transfer and Stake` or +/// `Delegate and Stake`. +pub trait StakeStrategy { + type Balance: frame_support::traits::tokens::Balance; + type AccountId: Clone + sp_std::fmt::Debug; + + /// Delegate to pool account. + /// + /// This is only used for first time delegation. For adding more delegation, use + /// [`Self::delegate_extra`]. + fn delegate( + who: &Self::AccountId, + pool_account: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Add more delegation to the pool account. + fn delegate_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Withdraw delegation from pool account to self. + fn withdraw_delegation( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; +} From 318eababb931a849b9530070960084fd2f0b8e23 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 00:35:59 +0100 Subject: [PATCH 167/202] add delegation related functions to staking interface --- .../frame/delegated-staking/src/tests.rs | 1 + substrate/frame/nomination-pools/src/mock.rs | 38 +++++++++++++++++++ substrate/primitives/staking/src/lib.rs | 26 +++++++++++++ 3 files changed, 65 insertions(+) diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 8de4d3f419fe..e58bec8bcf5b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -574,6 +574,7 @@ mod staking_integration { } } +// FIMXE(ank4n): Move these integration test to nomination pools. mod pool_integration { use super::*; use pallet_nomination_pools::{BondExtra, BondedPools, PoolState}; diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 85b8c3b64405..5feb9e42bbe0 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -228,6 +228,44 @@ impl sp_staking::StakingInterface for StakingMock { fn release_all(_who: &Self::AccountId) { unimplemented!("method currently not used in testing") } + + fn is_delegatee(who: &Self::AccountId) -> bool { + unimplemented!("method currently not used in testing") + } + + /// Effective balance of the delegatee account. + fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { + unimplemented!("method currently not used in testing") + } + + /// Delegate funds to `Delegatee`. + fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + unimplemented!("method currently not used in testing") + } + + /// Add more delegation to the pool account. + fn delegate_extra( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + unimplemented!("method currently not used in testing") + } + + /// Withdraw delegation from pool account to self. + fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + unimplemented!("method currently not used in testing") + } + + /// Does the delegatee have any pending slash. + fn has_pending_slash(delegatee: &Self::AccountId) -> bool { + unimplemented!("method currently not used in testing") + } + + fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { + unimplemented!("method currently not used in testing") + } + } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 99f78234a218..ef7ad329a4ca 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -170,6 +170,7 @@ pub trait StakingInterface { + MaxEncodedLen + FullCodec + TypeInfo + + Zero + Saturating; /// AccountId type used by the staking system. @@ -308,6 +309,31 @@ pub trait StakingInterface { #[cfg(feature = "runtime-benchmarks")] fn set_current_era(era: EraIndex); + + /// Returns true if who is a `delegatee` account. + fn is_delegatee(who: &Self::AccountId) -> bool; + + /// Effective balance of the delegatee account. + fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; + + /// Delegate funds to `Delegatee`. + fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + + /// Add more delegation to the pool account. + fn delegate_extra( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Withdraw delegation from pool account to self. + fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + + /// Does the delegatee have any pending slash. + fn has_pending_slash(delegatee: &Self::AccountId) -> bool; + + fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult; + } /// The amount of exposure for an era that an individual nominator has (susceptible to slashing). From aaa3cbc10589f9f97bf07bddeead19e2fdc9d1d6 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 00:50:11 +0100 Subject: [PATCH 168/202] staking interface impl updated --- .../frame/delegated-staking/src/impls.rs | 55 +++++++++++++++++++ substrate/frame/staking/src/pallet/impls.rs | 39 +++++++++++++ substrate/primitives/staking/src/lib.rs | 2 + 3 files changed, 96 insertions(+) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 2009c2e6596a..a349ca6c32de 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -194,6 +194,61 @@ impl StakingInterface for Pallet { fn set_current_era(era: EraIndex) { T::CoreStaking::set_current_era(era) } + + fn is_delegatee(who: &Self::AccountId) -> bool { + Self::is_delegate(who) + } + + /// Effective balance of the delegatee account. + fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { + Delegate::::from(who) + .map(|delegate| delegate.ledger.effective_balance()) + .unwrap_or_default() + } + + /// Delegate funds to `Delegatee`. + fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + Pallet::::register_as_delegate( + RawOrigin::Signed(delegatee.clone()).into(), + reward_account.clone(), + )?; + + // Delegate the funds from who to the pool account. + Pallet::::delegate_funds( + RawOrigin::Signed(who.clone()).into(), + delegatee.clone(), + amount, + ) + } + + /// Add more delegation to the pool account. + fn delegate_extra( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + Pallet::::delegate_funds( + RawOrigin::Signed(who.clone()).into(), + delegatee.clone(), + amount, + ) + } + + /// Withdraw delegation from pool account to self. + fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + Pallet::::release(RawOrigin::Signed(delegatee.clone()).into(), who.clone(), amount, 0) + } + + /// Does the delegatee have any pending slash. + fn has_pending_slash(delegatee: &Self::AccountId) -> bool { + Delegate::::from(delegatee) + .map(|d| !d.ledger.pending_slash.is_zero()) + .unwrap_or(false) + } + + fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { + Pallet::::do_slash(delegatee.clone(), delegator.clone(), value, maybe_reporter) + } } impl StakingDelegationSupport for Pallet { diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 3b2a7b512e2e..99f605bb9812 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1870,6 +1870,45 @@ impl StakingInterface for Pallet { fn release_all(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } + + fn is_delegatee(who: &Self::AccountId) -> bool { + defensive!("is_delegatee is not supported"); + false + } + + fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { + defensive!("delegatee_balance is not supported"); + BalanceOf::::zero() + } + + /// Delegate funds to `Delegatee`. + fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + Err(DispatchError::Other("delegate is not supported")) + } + + /// Add more delegation to the pool account. + fn delegate_extra( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + Err(DispatchError::Other("delegate is not supported")) + } + + /// Withdraw delegation from pool account to self. + fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + Err(DispatchError::Other("delegate is not supported")) + } + + /// Does the delegatee have any pending slash. + fn has_pending_slash(delegatee: &Self::AccountId) -> bool { + // slashing is greedy, so no pending slash. + false + } + + fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { + Err(DispatchError::Other("delegate is not supported")) + } } /// Standard implementation of `StakingDelegationSupport` that supports only direct staking and no diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index ef7ad329a4ca..6279756d566c 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -310,6 +310,8 @@ pub trait StakingInterface { #[cfg(feature = "runtime-benchmarks")] fn set_current_era(era: EraIndex); + // FIXME(ank4n): Break down this trait maybe? It is too bloated currently. + /// Returns true if who is a `delegatee` account. fn is_delegatee(who: &Self::AccountId) -> bool; From d80797945e6d2a3ba8fcb806f7347c93caa6bece Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 01:16:53 +0100 Subject: [PATCH 169/202] compiles but potentially buggy --- .../frame/delegated-staking/src/impls.rs | 89 +------------- .../frame/nomination-pools/src/adapter.rs | 113 ++++++++++++------ substrate/frame/nomination-pools/src/lib.rs | 25 ++-- substrate/frame/nomination-pools/src/mock.rs | 4 +- .../primitives/staking/src/delegation.rs | 57 +-------- 5 files changed, 95 insertions(+), 193 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index a349ca6c32de..893a2013ea1d 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -298,91 +298,4 @@ impl StakingDelegationSupport for Pallet { }, }); } -} - -impl PoolAdapter for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - /// Return balance of the `Delegate` (pool account) that is not bonded. - /// - /// Equivalent to [FunInspect::balance] for non delegate accounts. - fn transferable_balance(who: &Self::AccountId) -> Self::Balance { - Delegate::::from(who) - .map(|delegate| delegate.total_unbonded()) - .unwrap_or(Zero::zero()) - } - - /// Returns balance of account that is held. - /// - /// - For `delegate` accounts, this is their total delegation amount. - /// - For `delegator` accounts, this is their delegation amount. - fn total_balance(who: &Self::AccountId) -> Self::Balance { - if Self::is_delegator(who) { - return Delegation::::get(who).map(|d| d.amount).unwrap_or(Zero::zero()); - } - - Delegate::::from(who) - .map(|delegate| delegate.ledger.effective_balance()) - .unwrap_or(Zero::zero()) - } - - /// Add initial delegation to the pool account. - /// - /// Equivalent to [FunMutate::transfer] for Direct Staking. - fn delegate( - who: &Self::AccountId, - pool_account: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - // This is the first delegation so we needs to register the pool account as a `delegate`. - Pallet::::register_as_delegate( - RawOrigin::Signed(pool_account.clone()).into(), - reward_account.clone(), - )?; - - // Delegate the funds from who to the pool account. - Pallet::::delegate_funds( - RawOrigin::Signed(who.clone()).into(), - pool_account.clone(), - amount, - ) - } - - fn delegate_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - Pallet::::delegate_funds( - RawOrigin::Signed(who.clone()).into(), - pool_account.clone(), - amount, - ) - } - - fn release_delegation( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - // fixme(ank4n): This should not require slashing spans. - Pallet::::release(RawOrigin::Signed(pool_account.clone()).into(), who.clone(), amount, 0) - } - - fn has_pending_slash(delegate: &Self::AccountId) -> bool { - Delegate::::from(delegate) - .map(|d| !d.ledger.pending_slash.is_zero()) - .unwrap_or(false) - } - - fn delegator_slash( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, - ) -> DispatchResult { - Pallet::::do_slash(delegate.clone(), delegator.clone(), value, maybe_reporter) - } -} +} \ No newline at end of file diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index adcda1277198..7e7f9d26af81 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -17,14 +17,49 @@ use crate::*; +/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. +pub trait StakeAdapter { + type Balance: frame_support::traits::tokens::Balance; + type AccountId: Clone + sp_std::fmt::Debug; + + /// Balance that is free and can be released to delegator. + fn transferable_balance(who: &Self::AccountId) -> Self::Balance; + + /// Total balance of the account held for staking. + fn total_balance(who: &Self::AccountId) -> Self::Balance; + + /// Initiate delegation to the pool account. + fn bond( + who: &Self::AccountId, + pool_account: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Add more delegation to the pool account. + fn bond_extra( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Revoke delegation to pool account. + fn withdraw( + who: &Self::AccountId, + pool_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; +} + + /// Basic pool adapter that only supports Direct Staking. /// /// When delegating, tokens are moved between the delegator and pool account as opposed to holding /// tokens in delegator's accounts. -pub struct NoDelegation(PhantomData); +pub struct TransferStake(PhantomData); /// TODO(ankan) Call it FundManager/CurrencyAdapter/DelegationManager -impl PoolAdapter for NoDelegation { +impl StakeAdapter for TransferStake { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -41,7 +76,7 @@ impl PoolAdapter for NoDelegation { T::Currency::total_balance(who) } - fn delegate( + fn bond( who: &Self::AccountId, pool_account: &Self::AccountId, reward_account: &Self::AccountId, @@ -51,7 +86,7 @@ impl PoolAdapter for NoDelegation { T::Staking::bond(pool_account, amount, reward_account) } - fn delegate_extra( + fn bond_extra( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -60,7 +95,7 @@ impl PoolAdapter for NoDelegation { T::Staking::bond_extra(pool_account, amount) } - fn release_delegation( + fn withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -69,52 +104,60 @@ impl PoolAdapter for NoDelegation { Ok(()) } +} - fn has_pending_slash(_delegate: &Self::AccountId) -> bool { - // for direct staking, slashing is eager, and we don't need to do anything here. - false - } +pub struct DelegationStake(PhantomData); +impl StakeAdapter for DelegationStake { + type Balance = BalanceOf; + type AccountId = T::AccountId; - fn delegator_slash( - _delegate: &Self::AccountId, - _delegator: &Self::AccountId, - _value: Self::Balance, - _maybe_reporter: Option, - ) -> sp_runtime::DispatchResult { - // for direct staking, slashing is eager, and we don't need to do anything here. - defensive!("Delegator slash is not supported for direct staking"); - Err(Error::::NothingToSlash.into()) + /// Return balance of the `Delegate` (pool account) that is not bonded. + /// + /// Equivalent to [FunInspect::balance] for non delegate accounts. + fn transferable_balance(who: &Self::AccountId) -> Self::Balance { + T::Staking::delegatee_balance(who).saturating_sub(T::Staking::active_stake(who).unwrap_or_default()) } -} -/// Stake Strategy trait that can support different ways of staking such as `Transfer and Stake` or -/// `Delegate and Stake`. -pub trait StakeStrategy { - type Balance: frame_support::traits::tokens::Balance; - type AccountId: Clone + sp_std::fmt::Debug; + /// Returns balance of account that is held. + /// + /// - For `delegate` accounts, this is their total delegation amount. + /// - For `delegator` accounts, this is their delegation amount. + fn total_balance(who: &Self::AccountId) -> Self::Balance { + if T::Staking::is_delegatee(who) { + return T::Staking::delegatee_balance(who) + } + + // for delegators we return their held balance as well. + T::Currency::total_balance(who) + } - /// Delegate to pool account. + /// Add initial delegation to the pool account. /// - /// This is only used for first time delegation. For adding more delegation, use - /// [`Self::delegate_extra`]. - fn delegate( + /// Equivalent to [FunMutate::transfer] for Direct Staking. + fn bond( who: &Self::AccountId, pool_account: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance, - ) -> DispatchResult; + ) -> DispatchResult { + // This is the first delegation so we needs to register the pool account as a `delegate`. + T::Staking::delegate(who, pool_account, reward_account, amount) + } - /// Add more delegation to the pool account. - fn delegate_extra( + fn bond_extra( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, - ) -> DispatchResult; + ) -> DispatchResult { + T::Staking::delegate_extra(who, pool_account, amount) + } - /// Withdraw delegation from pool account to self. - fn withdraw_delegation( + fn withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, - ) -> DispatchResult; + ) -> DispatchResult { + // fixme(ank4n): This should not require slashing spans. + T::Staking::withdraw_delegation(who, pool_account, amount) + } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index e929666f06c5..3ea2fd47ff90 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -373,8 +373,9 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{delegation::PoolAdapter, EraIndex, StakingInterface}; +use sp_staking::{EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; +use adapter::StakeAdapter; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] use sp_runtime::TryRuntimeError; @@ -1056,7 +1057,7 @@ impl BondedPool { /// The pools balance that is transferable provided it is expendable by staking pallet. fn transferable_balance(&self) -> BalanceOf { - T::PoolAdapter::transferable_balance(&self.bonded_account()) + T::StakeAdapter::transferable_balance(&self.bonded_account()) } fn is_root(&self, who: &T::AccountId) -> bool { @@ -1262,11 +1263,11 @@ impl BondedPool { match ty { BondType::Create => - T::PoolAdapter::delegate(who, &bonded_account, &reward_account, amount)?, + T::StakeAdapter::bond(who, &bonded_account, &reward_account, amount)?, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. - BondType::Later => T::PoolAdapter::delegate_extra(who, &bonded_account, amount)?, + BondType::Later => T::StakeAdapter::bond_extra(who, &bonded_account, amount)?, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); @@ -1288,7 +1289,7 @@ impl BondedPool { } fn has_pending_slash(&self) -> bool { - T::PoolAdapter::has_pending_slash(&self.bonded_account()) + T::Staking::has_pending_slash(&self.bonded_account()) } } @@ -1652,7 +1653,7 @@ pub mod pallet { type MaxMetadataLen: Get; /// An adapter to support delegated to direct staking. - type PoolAdapter: PoolAdapter>; + type StakeAdapter: adapter::StakeAdapter>; } /// The sum of funds across all pools. @@ -2297,7 +2298,7 @@ pub mod pallet { // order to ensure members can leave the pool and it can be destroyed. .min(bonded_pool.transferable_balance()); - T::PoolAdapter::release_delegation( + T::StakeAdapter::withdraw( &member_account, &bonded_pool.bonded_account(), balance_to_unbond, @@ -2895,7 +2896,7 @@ impl Pallet { "could not transfer all amount to depositor while dissolving pool" ); defensive_assert!( - T::PoolAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), + T::StakeAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), "dissolving pool should not have any balance" ); // NOTE: Defensively force set balance to zero. @@ -3291,9 +3292,9 @@ impl Pallet { let member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; let bonded_account = Self::create_bonded_account(member.pool_id); - ensure!(T::PoolAdapter::has_pending_slash(&bonded_account), Error::::NothingToSlash); + ensure!(T::Staking::has_pending_slash(&bonded_account), Error::::NothingToSlash); - let delegated_balance = T::PoolAdapter::total_balance(&member_account); + let delegated_balance = T::StakeAdapter::total_balance(&member_account); let current_balance = member.total_balance(); defensive_assert!( delegated_balance >= current_balance, @@ -3303,7 +3304,7 @@ impl Pallet { // if nothing to slash, return error. ensure!(delegated_balance > current_balance, Error::::NothingToSlash); - T::PoolAdapter::delegator_slash( + T::Staking::delegator_slash( &bonded_account, &member_account, delegated_balance.defensive_saturating_sub(current_balance), @@ -3505,7 +3506,7 @@ impl Pallet { let sum_unbonding_balance = subs.sum_unbonding_balance(); let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default(); - let total_balance = T::PoolAdapter::total_balance(&pool_account); + let total_balance = T::StakeAdapter::total_balance(&pool_account); assert!( total_balance >= bonded_balance + sum_unbonding_balance, diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 5feb9e42bbe0..b3bcc6f41c75 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -259,7 +259,7 @@ impl sp_staking::StakingInterface for StakingMock { /// Does the delegatee have any pending slash. fn has_pending_slash(delegatee: &Self::AccountId) -> bool { - unimplemented!("method currently not used in testing") + false } fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { @@ -349,7 +349,7 @@ impl pools::Config for Runtime { type MaxMetadataLen = MaxMetadataLen; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type PoolAdapter = adapter::NoDelegation; + type StakeAdapter = adapter::TransferStake; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 40e06ab5157e..0f18f32f3ccc 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -53,59 +53,4 @@ pub trait StakingDelegationSupport { /// Reports an ongoing slash to the `delegate` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); -} - -/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. -pub trait PoolAdapter { - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - type AccountId: Clone + sp_std::fmt::Debug; - - /// Balance that is free and can be released to delegator. - fn transferable_balance(who: &Self::AccountId) -> Self::Balance; - - /// Total balance of the account held for staking. - fn total_balance(who: &Self::AccountId) -> Self::Balance; - - /// Initiate delegation to the pool account. - fn delegate( - who: &Self::AccountId, - pool_account: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Add more delegation to the pool account. - fn delegate_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Revoke delegation to pool account. - fn release_delegation( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Returns true if the `delegate` has pending slash to be applied. - fn has_pending_slash(delegate: &Self::AccountId) -> bool; - - /// Apply a slash to the `delegator`. - /// - /// This is called when the corresponding `delegate` has pending slash to be applied. - fn delegator_slash( - delegate: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, - ) -> DispatchResult; -} +} \ No newline at end of file From 2668e783dacb14fbf983de28407081eb443bbff4 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 01:25:25 +0100 Subject: [PATCH 170/202] one test failing --- substrate/frame/delegated-staking/src/impls.rs | 1 - substrate/frame/delegated-staking/src/mock.rs | 2 +- substrate/frame/nomination-pools/src/adapter.rs | 6 +++--- substrate/frame/nomination-pools/src/lib.rs | 2 +- substrate/frame/nomination-pools/test-staking/src/mock.rs | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 893a2013ea1d..bc8d41034cae 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -19,7 +19,6 @@ //! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. use super::*; -use sp_staking::delegation::PoolAdapter; /// StakingInterface implementation with delegation support. /// diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 439dc23df6e4..4d0388a7f6bc 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -181,7 +181,7 @@ impl pallet_nomination_pools::Config for Runtime { type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type PoolAdapter = DelegatedStaking; + type StakeAdapter = pallet_nomination_pools::adapter::DelegationStake; } frame_support::construct_runtime!( diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 7e7f9d26af81..9af228a04611 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -44,7 +44,7 @@ pub trait StakeAdapter { ) -> DispatchResult; /// Revoke delegation to pool account. - fn withdraw( + fn claim_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -95,7 +95,7 @@ impl StakeAdapter for TransferStake { T::Staking::bond_extra(pool_account, amount) } - fn withdraw( + fn claim_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -152,7 +152,7 @@ impl StakeAdapter for DelegationStake { T::Staking::delegate_extra(who, pool_account, amount) } - fn withdraw( + fn claim_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 3ea2fd47ff90..702bcc94c2c1 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -2298,7 +2298,7 @@ pub mod pallet { // order to ensure members can leave the pool and it can be destroyed. .min(bonded_pool.transferable_balance()); - T::StakeAdapter::withdraw( + T::StakeAdapter::claim_withdraw( &member_account, &bonded_pool.bonded_account(), balance_to_unbond, diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 60b782d5b9ac..c8446a6e077a 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -187,7 +187,7 @@ impl pallet_nomination_pools::Config for Runtime { type MaxUnbonding = ConstU32<8>; type MaxPointsToBalance = ConstU8<10>; type PalletId = PoolsPalletId; - type PoolAdapter = pallet_nomination_pools::adapter::NoDelegation; + type StakeAdapter = pallet_nomination_pools::adapter::TransferStake; } type Block = frame_system::mocking::MockBlock; From c8fc6a1ba1b7e7cf7d5e659ea889a4b03d7cd7aa Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 02:03:26 +0100 Subject: [PATCH 171/202] all test passes --- substrate/frame/delegated-staking/src/impls.rs | 4 ++++ substrate/frame/delegated-staking/src/lib.rs | 8 ++------ substrate/frame/delegated-staking/src/types.rs | 2 +- substrate/frame/nomination-pools/src/adapter.rs | 7 +------ substrate/frame/nomination-pools/src/lib.rs | 2 +- substrate/frame/nomination-pools/src/mock.rs | 4 ++++ substrate/frame/staking/src/pallet/impls.rs | 5 +++++ substrate/primitives/staking/src/lib.rs | 2 ++ 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index bc8d41034cae..62f67f390531 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -248,6 +248,10 @@ impl StakingInterface for Pallet { fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { Pallet::::do_slash(delegatee.clone(), delegator.clone(), value, maybe_reporter) } + + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { + Delegation::::get(delegator).map(|d| d.amount).unwrap_or_default() + } } impl StakingDelegationSupport for Pallet { diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index d58696d14b1a..41ef8a3a0b2c 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -582,7 +582,6 @@ impl Pallet { }; Delegation::::from(delegate, new_delegation_amount).save(delegator); - ledger.total_delegated = ledger.total_delegated.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; ledger.save(delegate); @@ -630,11 +629,8 @@ impl Pallet { .defensive_ok_or(ArithmeticError::Overflow)?; // remove delegator if nothing delegated anymore - if delegation.amount == BalanceOf::::zero() { - >::remove(delegator); - } else { - delegation.save(delegator); - } + delegation.save(delegator); + let released = T::Currency::release( &HoldReason::Delegating.into(), diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 30c700df3820..add4ae21847e 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -75,7 +75,7 @@ impl Delegation { // Clean up if no delegation left. if self.amount == Zero::zero() { >::remove(key); - return; + return } >::insert(key, self) diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 9af228a04611..f4f5e18a2c9b 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -123,12 +123,7 @@ impl StakeAdapter for DelegationStake { /// - For `delegate` accounts, this is their total delegation amount. /// - For `delegator` accounts, this is their delegation amount. fn total_balance(who: &Self::AccountId) -> Self::Balance { - if T::Staking::is_delegatee(who) { - return T::Staking::delegatee_balance(who) - } - - // for delegators we return their held balance as well. - T::Currency::total_balance(who) + T::Staking::delegatee_balance(who) } /// Add initial delegation to the pool account. diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 702bcc94c2c1..500ca0564314 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -3294,7 +3294,7 @@ impl Pallet { let bonded_account = Self::create_bonded_account(member.pool_id); ensure!(T::Staking::has_pending_slash(&bonded_account), Error::::NothingToSlash); - let delegated_balance = T::StakeAdapter::total_balance(&member_account); + let delegated_balance = T::Staking::delegated_balance(&member_account); let current_balance = member.total_balance(); defensive_assert!( delegated_balance >= current_balance, diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index b3bcc6f41c75..cc5eec42cfff 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -266,6 +266,10 @@ impl sp_staking::StakingInterface for StakingMock { unimplemented!("method currently not used in testing") } + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { + unimplemented!("method currently not used in testing") + } + } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 99f605bb9812..cb26c3c8009c 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1909,6 +1909,11 @@ impl StakingInterface for Pallet { fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { Err(DispatchError::Other("delegate is not supported")) } + + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { + defensive!("delegated_balance is not supported"); + BalanceOf::::zero() + } } /// Standard implementation of `StakingDelegationSupport` that supports only direct staking and no diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 6279756d566c..1515cd0138ad 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -336,6 +336,8 @@ pub trait StakingInterface { fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult; + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; + } /// The amount of exposure for an era that an individual nominator has (susceptible to slashing). From d41fa5343ffc5bb36438569e93e63ce6f00c615d Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 02:18:28 +0100 Subject: [PATCH 172/202] rename trait method --- .../frame/nomination-pools/src/adapter.rs | 32 ++++++++++--------- substrate/frame/nomination-pools/src/lib.rs | 4 ++- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index f4f5e18a2c9b..41e0581d8489 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -22,8 +22,8 @@ pub trait StakeAdapter { type Balance: frame_support::traits::tokens::Balance; type AccountId: Clone + sp_std::fmt::Debug; - /// Balance that is free and can be released to delegator. - fn transferable_balance(who: &Self::AccountId) -> Self::Balance; + /// Balance of the account. + fn balance(who: &Self::AccountId) -> Self::Balance; /// Total balance of the account held for staking. fn total_balance(who: &Self::AccountId) -> Self::Balance; @@ -51,7 +51,6 @@ pub trait StakeAdapter { ) -> DispatchResult; } - /// Basic pool adapter that only supports Direct Staking. /// /// When delegating, tokens are moved between the delegator and pool account as opposed to holding @@ -63,13 +62,13 @@ impl StakeAdapter for TransferStake { type Balance = BalanceOf; type AccountId = T::AccountId; - fn transferable_balance(who: &Self::AccountId) -> Self::Balance { + fn balance(who: &Self::AccountId) -> Self::Balance { // Note on why we can't use `Currency::reducible_balance`: Since pooled account has a // provider (staking pallet), the account can not be set expendable by // `pallet-nomination-pool`. This means reducible balance always returns balance preserving // ED in the account. What we want though is transferable balance given the account can be // dusted. - T::Currency::balance(who).saturating_sub(T::Staking::active_stake(who).unwrap_or_default()) + T::Currency::balance(who) } fn total_balance(who: &Self::AccountId) -> Self::Balance { @@ -107,23 +106,26 @@ impl StakeAdapter for TransferStake { } pub struct DelegationStake(PhantomData); + impl StakeAdapter for DelegationStake { type Balance = BalanceOf; type AccountId = T::AccountId; - /// Return balance of the `Delegate` (pool account) that is not bonded. - /// - /// Equivalent to [FunInspect::balance] for non delegate accounts. - fn transferable_balance(who: &Self::AccountId) -> Self::Balance { - T::Staking::delegatee_balance(who).saturating_sub(T::Staking::active_stake(who).unwrap_or_default()) + fn balance(who: &Self::AccountId) -> Self::Balance { + if T::Staking::is_delegatee(who) { + return T::Staking::delegatee_balance(who) + } + + T::Currency::balance(who) } - /// Returns balance of account that is held. - /// - /// - For `delegate` accounts, this is their total delegation amount. - /// - For `delegator` accounts, this is their delegation amount. + /// Return total balance of the account. fn total_balance(who: &Self::AccountId) -> Self::Balance { - T::Staking::delegatee_balance(who) + if T::Staking::is_delegatee(who) { + return T::Staking::delegatee_balance(who); + } + + T::Currency::total_balance(who) } /// Add initial delegation to the pool account. diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 500ca0564314..10e6fc69a04c 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1057,7 +1057,9 @@ impl BondedPool { /// The pools balance that is transferable provided it is expendable by staking pallet. fn transferable_balance(&self) -> BalanceOf { - T::StakeAdapter::transferable_balance(&self.bonded_account()) + let account = self.bonded_account(); + T::StakeAdapter::balance(&account) + .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) } fn is_root(&self, who: &T::AccountId) -> bool { From c408581a624044beb296c71d83ad704e8e70677d Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 02:33:08 +0100 Subject: [PATCH 173/202] minor refactors --- .../frame/delegated-staking/src/impls.rs | 3 +- substrate/frame/delegated-staking/src/lib.rs | 2 +- .../frame/nomination-pools/src/adapter.rs | 35 ++++++++++--------- substrate/frame/nomination-pools/src/mock.rs | 2 +- substrate/frame/staking/src/ledger.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 2 +- substrate/primitives/staking/src/lib.rs | 4 +-- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 62f67f390531..141a8651503f 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -171,7 +171,7 @@ impl StakingInterface for Pallet { T::CoreStaking::slash_reward_fraction() } - fn release_all(_who: &Self::AccountId) { + fn unsafe_release_all(_who: &Self::AccountId) { defensive_assert!(false, "not supported for delegated impl of staking interface"); } @@ -235,6 +235,7 @@ impl StakingInterface for Pallet { /// Withdraw delegation from pool account to self. fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + // fixme(ank4n): This should not require slashing spans. Pallet::::release(RawOrigin::Signed(delegatee.clone()).into(), who.clone(), amount, 0) } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 41ef8a3a0b2c..95242fe60f9d 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -535,7 +535,7 @@ impl Pallet { let stake = T::CoreStaking::stake(who)?; // release funds from core staking. - T::CoreStaking::release_all(who); + T::CoreStaking::unsafe_release_all(who); // transferring just released staked amount. This should never fail but if it does, it // indicates bad state and we abort. diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 41e0581d8489..0bf042fd47e7 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -17,7 +17,11 @@ use crate::*; -/// Pool adapter trait that can support multiple modes of staking: i.e. Delegated or Direct. +/// An adapter trait that can support multiple staking strategies: e.g. `Transfer and stake` or +/// `delegation and stake`. +/// +/// This trait is very specific to nomination pool and meant to make switching between different +/// staking implementations easier. pub trait StakeAdapter { type Balance: frame_support::traits::tokens::Balance; type AccountId: Clone + sp_std::fmt::Debug; @@ -25,10 +29,10 @@ pub trait StakeAdapter { /// Balance of the account. fn balance(who: &Self::AccountId) -> Self::Balance; - /// Total balance of the account held for staking. + /// Total balance of the account. fn total_balance(who: &Self::AccountId) -> Self::Balance; - /// Initiate delegation to the pool account. + /// Bond delegator via the pool account. fn bond( who: &Self::AccountId, pool_account: &Self::AccountId, @@ -36,14 +40,14 @@ pub trait StakeAdapter { amount: Self::Balance, ) -> DispatchResult; - /// Add more delegation to the pool account. + /// Add more bond via the pool account. fn bond_extra( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - /// Revoke delegation to pool account. + /// Claim withdrawn amount in the pool account. fn claim_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, @@ -51,13 +55,11 @@ pub trait StakeAdapter { ) -> DispatchResult; } -/// Basic pool adapter that only supports Direct Staking. +/// An adapter implementation that supports transfer based staking /// -/// When delegating, tokens are moved between the delegator and pool account as opposed to holding -/// tokens in delegator's accounts. +/// The funds are transferred to the pool account and then staked via the pool account. pub struct TransferStake(PhantomData); -/// TODO(ankan) Call it FundManager/CurrencyAdapter/DelegationManager impl StakeAdapter for TransferStake { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -105,6 +107,10 @@ impl StakeAdapter for TransferStake { } } +/// An adapter implementation that supports delegation based staking +/// +/// The funds are delegated from pool account to a delegatee and then staked. The advantage of this +/// approach is that the funds are held in the delegator account and not in the pool account. pub struct DelegationStake(PhantomData); impl StakeAdapter for DelegationStake { @@ -112,14 +118,15 @@ impl StakeAdapter for DelegationStake { type AccountId = T::AccountId; fn balance(who: &Self::AccountId) -> Self::Balance { + // Pool account is a delegatee, and its balance is the sum of all member delegations towards + // it. if T::Staking::is_delegatee(who) { - return T::Staking::delegatee_balance(who) + return T::Staking::delegatee_balance(who); } T::Currency::balance(who) } - /// Return total balance of the account. fn total_balance(who: &Self::AccountId) -> Self::Balance { if T::Staking::is_delegatee(who) { return T::Staking::delegatee_balance(who); @@ -128,16 +135,13 @@ impl StakeAdapter for DelegationStake { T::Currency::total_balance(who) } - /// Add initial delegation to the pool account. - /// - /// Equivalent to [FunMutate::transfer] for Direct Staking. fn bond( who: &Self::AccountId, pool_account: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - // This is the first delegation so we needs to register the pool account as a `delegate`. + // For delegation staking, we just delegate the funds to pool account. T::Staking::delegate(who, pool_account, reward_account, amount) } @@ -154,7 +158,6 @@ impl StakeAdapter for DelegationStake { pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - // fixme(ank4n): This should not require slashing spans. T::Staking::withdraw_delegation(who, pool_account, amount) } } diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index cc5eec42cfff..be4c644beea3 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -225,7 +225,7 @@ impl sp_staking::StakingInterface for StakingMock { unimplemented!("method currently not used in testing") } - fn release_all(_who: &Self::AccountId) { + fn unsafe_release_all(_who: &Self::AccountId) { unimplemented!("method currently not used in testing") } diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index eed459500a64..afab32366f9d 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -207,7 +207,7 @@ impl StakingLedger { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { - Pallet::::release_all(&ledger.stash); + Pallet::::unsafe_release_all(&ledger.stash); Ledger::::remove(controller); >::remove(&stash); diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index cb26c3c8009c..33387817b95c 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1867,7 +1867,7 @@ impl StakingInterface for Pallet { SlashRewardFraction::::get() } - fn release_all(who: &Self::AccountId) { + fn unsafe_release_all(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 1515cd0138ad..0e1384442106 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -294,8 +294,8 @@ pub trait StakingInterface { /// Release all funds bonded for stake. /// - /// Unsafe, only used for migration of `delegate` accounts. - fn release_all(who: &Self::AccountId); + /// Unsafe, only used for migration of `delegatee` accounts. + fn unsafe_release_all(who: &Self::AccountId); #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> Page; From d95a6febebcca3e369d632cff15d6bb204724a08 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 03:15:48 +0100 Subject: [PATCH 174/202] rename delegate to delegatee --- .../frame/delegated-staking/src/impls.rs | 50 +-- substrate/frame/delegated-staking/src/lib.rs | 213 +++++++------ substrate/frame/delegated-staking/src/mock.rs | 22 +- .../frame/delegated-staking/src/tests.rs | 290 +++++++++--------- .../frame/delegated-staking/src/types.rs | 54 ++-- substrate/frame/staking/src/pallet/impls.rs | 16 +- substrate/frame/staking/src/slashing.rs | 2 +- .../primitives/staking/src/delegation.rs | 4 +- 8 files changed, 323 insertions(+), 328 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 141a8651503f..585378e9c74e 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -23,7 +23,7 @@ use super::*; /// StakingInterface implementation with delegation support. /// /// Only supports Nominators via Delegated Bonds. It is possible for a nominator to migrate and -/// become a `Delegate`. +/// become a `delegatee`. impl StakingInterface for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; @@ -53,12 +53,12 @@ impl StakingInterface for Pallet { } fn stake(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegate(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotSupported); return T::CoreStaking::stake(who); } fn total_stake(who: &Self::AccountId) -> Result { - if Self::is_delegate(who) { + if Self::is_delegatee(who) { return T::CoreStaking::total_stake(who); } @@ -79,7 +79,7 @@ impl StakingInterface for Pallet { } fn fully_unbond(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotSupported); return T::CoreStaking::fully_unbond(who); } @@ -89,35 +89,35 @@ impl StakingInterface for Pallet { payee: &Self::AccountId, ) -> DispatchResult { // ensure who is not already staked - ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegate); - let delegate = Delegate::::from(who)?; + ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegatee); + let delegatee = Delegatee::::from(who)?; - ensure!(delegate.available_to_bond() >= value, Error::::NotEnoughFunds); - ensure!(delegate.ledger.payee == *payee, Error::::InvalidRewardDestination); + ensure!(delegatee.available_to_bond() >= value, Error::::NotEnoughFunds); + ensure!(delegatee.ledger.payee == *payee, Error::::InvalidRewardDestination); T::CoreStaking::bond(who, value, payee) } fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotSupported); return T::CoreStaking::nominate(who, validators); } fn chill(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegate(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotSupported); return T::CoreStaking::chill(who); } fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { - let delegation_register = >::get(who).ok_or(Error::::NotDelegate)?; + let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; ensure!(delegation_register.stakeable_balance() >= extra, Error::::NotEnoughFunds); T::CoreStaking::bond_extra(who, extra) } fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult { - let delegate = Delegate::::from(stash)?; - ensure!(delegate.bonded_stake() >= value, Error::::NotEnoughFunds); + let delegatee = Delegatee::::from(stash)?; + ensure!(delegatee.bonded_stake() >= value, Error::::NotEnoughFunds); T::CoreStaking::unbond(stash, value) } @@ -130,7 +130,7 @@ impl StakingInterface for Pallet { num_slashing_spans: u32, ) -> Result { Pallet::::withdraw_unbonded(&pool_acc, num_slashing_spans) - .map(|delegate| delegate.ledger.total_delegated.is_zero()) + .map(|delegatee| delegatee.ledger.total_delegated.is_zero()) } fn desired_validator_count() -> u32 { @@ -154,7 +154,7 @@ impl StakingInterface for Pallet { } fn status(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegate(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotSupported); T::CoreStaking::status(who) } @@ -195,24 +195,24 @@ impl StakingInterface for Pallet { } fn is_delegatee(who: &Self::AccountId) -> bool { - Self::is_delegate(who) + Self::is_delegatee(who) } /// Effective balance of the delegatee account. fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { - Delegate::::from(who) - .map(|delegate| delegate.ledger.effective_balance()) + Delegatee::::from(who) + .map(|delegatee| delegatee.ledger.effective_balance()) .unwrap_or_default() } /// Delegate funds to `Delegatee`. fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - Pallet::::register_as_delegate( + Pallet::::register_as_delegatee( RawOrigin::Signed(delegatee.clone()).into(), reward_account.clone(), )?; - // Delegate the funds from who to the pool account. + // Delegate the funds from who to the delegatee account. Pallet::::delegate_funds( RawOrigin::Signed(who.clone()).into(), delegatee.clone(), @@ -241,7 +241,7 @@ impl StakingInterface for Pallet { /// Does the delegatee have any pending slash. fn has_pending_slash(delegatee: &Self::AccountId) -> bool { - Delegate::::from(delegatee) + Delegatee::::from(delegatee) .map(|d| !d.ledger.pending_slash.is_zero()) .unwrap_or(false) } @@ -262,8 +262,8 @@ impl StakingDelegationSupport for Pallet { /// this balance is total delegator that can be staked, and importantly not extra balance that /// is delegated but not bonded yet. fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - Delegate::::from(who) - .map(|delegate| delegate.ledger.stakeable_balance()) + Delegatee::::from(who) + .map(|delegatee| delegatee.ledger.stakeable_balance()) .unwrap_or_default() } @@ -290,8 +290,8 @@ impl StakingDelegationSupport for Pallet { register.payee != reward_acc } - fn is_delegate(who: &Self::AccountId) -> bool { - Self::is_delegate(who) + fn is_delegatee(who: &Self::AccountId) -> bool { + Self::is_delegatee(who) } fn report_slash(who: &Self::AccountId, slash: Self::Balance) { diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 95242fe60f9d..8890b2f261d0 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -17,7 +17,7 @@ //! # Delegated Staking Pallet //! -//! An abstraction over staking pallet to support delegation of funds to a `delegate` account which +//! An abstraction over staking pallet to support delegation of funds to a `delegatee` account which //! can use all the delegated funds to it in the staking pallet as if its own fund. //! //! NOTE: The pallet exposes extrinsics which are not yet meant to be exposed in the runtime but @@ -34,40 +34,40 @@ //! //! #### Reward and Slashing //! This pallet does not enforce any specific strategy for how rewards or slashes are applied. It -//! is upto the `delegate` account to decide how to apply the rewards and slashes. +//! is upto the `delegatee` account to decide how to apply the rewards and slashes. //! //! This importantly allows clients of this pallet to build their own strategies for reward/slashes. -//! For example, a `delegate` account can choose to first slash the reward pot before slashing the +//! For example, a `delegatee` account can choose to first slash the reward pot before slashing the //! delegators. Or part of the reward can go to a insurance fund that can be used to cover any //! potential future slashes. The goal is to eventually allow foreign MultiLocations //! (smart contracts or pallets on another chain) to build their own pooled staking solutions //! similar to `NominationPool`. //! //! ## Key Terminologies -//! - *Delegate*: An account who accepts delegations from other accounts (called `Delegators`). -//! - *Delegator*: An account who delegates their funds to a `delegate`. -//! - *DelegationLedger*: A data structure that stores important information about the `delegate` +//! - *delegatee*: An account who accepts delegations from other accounts (called `Delegators`). +//! - *Delegator*: An account who delegates their funds to a `delegatee`. +//! - *DelegationLedger*: A data structure that stores important information about the `delegatee` //! such as their total delegated stake. -//! - *Delegation*: A data structure that stores the amount of funds delegated to a `delegate` by a +//! - *Delegation*: A data structure that stores the amount of funds delegated to a `delegatee` by a //! `delegator`. //! //! ## Interface //! //! #### Dispatchable Calls //! The pallet exposes the following [`Call`]s: -//! - `register_as_delegate`: Register an account to be a `Delegate`. Once an account is registered -//! as a `Delegate`, for staking operations, only its delegated funds are used. This means it +//! - `register_as_delegatee`: Register an account to be a `delegatee`. Once an account is registered +//! as a `delegatee`, for staking operations, only its delegated funds are used. This means it //! cannot use its own free balance to stake. -//! - `migrate_to_delegate`: This allows a `Nominator` account to become a `Delegate` account. +//! - `migrate_to_delegate`: This allows a `Nominator` account to become a `delegatee` account. //! Explained in more detail in the `Migration` section. //! - `release`: Release funds to `delegator` from `unclaimed_withdrawals` register of the -//! `delegate`. +//! `delegatee`. //! - `migrate_delegation`: Migrate delegated funds from one account to another. This is useful for -//! example, delegators to a pool account which has migrated to be `delegate` to migrate their +//! example, delegators to a pool account which has migrated to be `delegatee` to migrate their //! funds from pool account back to their own account and delegated to pool as a `delegator`. Once //! the funds are migrated, the `delegator` can use the funds for other purposes which allows //! usage of held funds in an account, such as governance. -//! - `delegate_funds`: Delegate funds to a `Delegate` account and update the bond to staking. +//! - `delegate_funds`: Delegate funds to a `delegatee` account and update the bond to staking. //! //! #### [Staking Interface](StakingInterface) //! This pallet reimplements the staking interface as a wrapper implementation over @@ -78,33 +78,28 @@ //! The pallet implements the staking delegation support trait which staking pallet can use to //! provide compatibility with this pallet. //! -//! #### [Pool Adapter](sp_staking::delegation::PoolAdapter) -//! The pallet also implements the pool adapter trait which allows NominationPool to use this pallet -//! to support delegation based staking from pool accounts. This strategy also allows the pool to -//! switch implementations while having minimal changes to its own logic. -//! //! ## Lazy Slashing //! One of the reasons why direct nominators on staking pallet cannot scale well is because all //! nominators are slashed at the same time. This is expensive and needs to be bounded operation. //! -//! This pallet implements a lazy slashing mechanism. Any slashes to a `delegate` are posted in its +//! This pallet implements a lazy slashing mechanism. Any slashes to a `delegatee` are posted in its //! `DelegationLedger` as a pending slash. Since the actual amount is held in the multiple -//! `delegator` accounts, this pallet has no way to know how to apply slash. It is `delegate`'s +//! `delegator` accounts, this pallet has no way to know how to apply slash. It is `delegatee`'s //! responsibility to apply slashes for each delegator, one at a time. Staking pallet ensures the //! pending slash never exceeds staked amount and would freeze further withdraws until pending //! slashes are applied. //! //! `NominationPool` can apply slash for all its members by calling -//! [PoolAdapter::delegator_slash](sp_staking::delegation::PoolAdapter::delegator_slash). +//! [StakingInterface::delegator_slash](sp_staking::StakingInterface::delegator_slash). //! //! ## Migration from Nominator to Delegate //! More details [here](https://hackmd.io/@ak0n/np-delegated-staking-migration). //! //! ## Reward Destination Restrictions -//! This pallets set an important restriction of rewards account to be separate from `delegate` -//! account. This is because, `delegate` balance is not what is directly exposed but the funds that -//! are delegated to it. For `delegate` accounts, we have also no way to auto-compound rewards. The -//! rewards need to be paid out to delegators and then delegated again to the `delegate` account. +//! This pallets set an important restriction of rewards account to be separate from `delegatee` +//! account. This is because, `delegatee` balance is not what is directly exposed but the funds that +//! are delegated to it. For `delegatee` accounts, we have also no way to auto-compound rewards. The +//! rewards need to be paid out to delegators and then delegated again to the `delegatee` account. //! //! ## Nomination Pool vs Delegation Staking //! This pallet is not a replacement for Nomination Pool but adds a new primitive over staking @@ -220,9 +215,9 @@ pub mod pallet { pub enum Error { /// The account cannot perform this operation. NotAllowed, - /// An existing staker cannot become a `delegate`. + /// An existing staker cannot become a `delegatee`. AlreadyStaker, - /// Reward Destination cannot be `delegate` account. + /// Reward Destination cannot be `delegatee` account. InvalidRewardDestination, /// Delegation conditions are not met. /// @@ -232,13 +227,13 @@ pub mod pallet { InvalidDelegation, /// The account does not have enough funds to perform the operation. NotEnoughFunds, - /// Not an existing `delegate` account. - NotDelegate, + /// Not an existing `delegatee` account. + NotDelegatee, /// Not a Delegator account. NotDelegator, /// Some corruption in internal state. BadState, - /// Unapplied pending slash restricts operation on `delegate`. + /// Unapplied pending slash restricts operation on `delegatee`. UnappliedSlash, /// Failed to withdraw amount from Core Staking. WithdrawFailed, @@ -260,16 +255,16 @@ pub mod pallet { #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { /// Funds delegated by a delegator. - Delegated { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Delegated { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, /// Funds released to a delegator. - Released { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Released { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, /// Funds slashed from a delegator. - Slashed { delegate: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, + Slashed { delegatee: T::AccountId, delegator: T::AccountId, amount: BalanceOf }, } /// Map of Delegators to their `Delegation`. /// - /// Implementation note: We are not using a double map with `delegator` and `delegate` account + /// Implementation note: We are not using a double map with `delegator` and `delegatee` account /// as keys since we want to restrict delegators to delegate only to one account at a time. #[pallet::storage] pub(crate) type Delegators = @@ -288,22 +283,22 @@ pub mod pallet { /// behalf. #[pallet::call_index(0)] #[pallet::weight(Weight::default())] - pub fn register_as_delegate( + pub fn register_as_delegatee( origin: OriginFor, reward_account: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; - // Existing `delegate` cannot register again. - ensure!(!Self::is_delegate(&who), Error::::NotAllowed); + // Existing `delegatee` cannot register again. + ensure!(!Self::is_delegatee(&who), Error::::NotAllowed); - // A delegator cannot become a `delegate`. + // A delegator cannot become a `delegatee`. ensure!(!Self::is_delegator(&who), Error::::NotAllowed); // They cannot be already a direct staker in the staking pallet. ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); - // Reward account cannot be same as `delegate` account. + // Reward account cannot be same as `delegatee` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); Self::do_register_delegator(&who, &reward_account); @@ -325,26 +320,26 @@ pub mod pallet { /// claim back their share of delegated funds from `proxy_delegator` to self. #[pallet::call_index(1)] #[pallet::weight(Weight::default())] - pub fn migrate_to_delegate( + pub fn migrate_to_delegatee( origin: OriginFor, reward_account: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; - // ensure who is not already a delegate. - ensure!(!Self::is_delegate(&who), Error::::NotAllowed); + // ensure who is not already a delegatee. + ensure!(!Self::is_delegatee(&who), Error::::NotAllowed); // and they should already be a nominator in `CoreStaking`. ensure!(Self::is_direct_nominator(&who), Error::::NotAllowed); - // Reward account cannot be same as `delegate` account. + // Reward account cannot be same as `delegatee` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); - Self::do_migrate_to_delegate(&who, &reward_account) + Self::do_migrate_to_delegatee(&who, &reward_account) } /// Release delegated amount to delegator. /// - /// This can be called by existing `delegate` accounts. + /// This can be called by existing `delegatee` accounts. /// /// Tries to withdraw unbonded fund from `CoreStaking` if needed and release amount to /// `delegator`. @@ -362,7 +357,7 @@ pub mod pallet { /// Migrate delegated fund. /// - /// This can be called by migrating `delegate` accounts. + /// This can be called by migrating `delegatee` accounts. /// /// This moves delegator funds from `pxoxy_delegator` account to `delegator` account. #[pallet::call_index(3)] @@ -372,21 +367,21 @@ pub mod pallet { delegator: T::AccountId, amount: BalanceOf, ) -> DispatchResult { - let delegate = ensure_signed(origin)?; + let delegatee = ensure_signed(origin)?; // Ensure they have minimum delegation. ensure!(amount >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); // Ensure delegator is sane. - ensure!(!Self::is_delegate(&delegator), Error::::NotAllowed); + ensure!(!Self::is_delegatee(&delegator), Error::::NotAllowed); ensure!(!Self::is_delegator(&delegator), Error::::NotAllowed); ensure!(Self::not_direct_staker(&delegator), Error::::AlreadyStaker); // ensure delegate is sane. - ensure!(Self::is_delegate(&delegate), Error::::NotDelegate); + ensure!(Self::is_delegatee(&delegatee), Error::::NotDelegatee); // and has enough delegated balance to migrate. - let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, delegate); + let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, delegatee); let balance_remaining = Self::held_balance_of(&proxy_delegator); ensure!(balance_remaining >= amount, Error::::NotEnoughFunds); @@ -400,7 +395,7 @@ pub mod pallet { #[pallet::weight(Weight::default())] pub fn delegate_funds( origin: OriginFor, - delegate: T::AccountId, + delegatee: T::AccountId, amount: BalanceOf, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -409,12 +404,12 @@ pub mod pallet { ensure!(amount > T::Currency::minimum_balance(), Error::::NotEnoughFunds); // ensure delegator is sane. - ensure!(Delegation::::can_delegate(&who, &delegate), Error::::InvalidDelegation); + ensure!(Delegation::::can_delegate(&who, &delegatee), Error::::InvalidDelegation); ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); // ensure delegate is sane. ensure!( - DelegationLedger::::can_accept_delegation(&delegate), + DelegationLedger::::can_accept_delegation(&delegatee), Error::::NotAcceptingDelegations ); @@ -423,23 +418,23 @@ pub mod pallet { ensure!(delegator_balance >= amount, Error::::NotEnoughFunds); // add to delegation - Self::do_delegate(&who, &delegate, amount)?; + Self::do_delegate(&who, &delegatee, amount)?; // bond the amount to `CoreStaking`. - Self::do_bond(&delegate, amount) + Self::do_bond(&delegatee, amount) } /// Toggle delegate status to start or stop accepting new delegations. /// /// This can only be used by existing delegates. If not a delegate yet, use - /// [Call::register_as_delegate] first. + /// [Call::register_as_delegatee] first. #[pallet::call_index(5)] #[pallet::weight(Weight::default())] - pub fn toggle_delegate_status(origin: OriginFor) -> DispatchResult { + pub fn toggle_delegatee_status(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - let delegate = Delegate::::from(&who)?; - let should_block = !delegate.ledger.blocked; - delegate.update_status(should_block).save(); + let delegatee = Delegatee::::from(&who)?; + let should_block = !delegatee.ledger.blocked; + delegatee.update_status(should_block).save(); Ok(()) } @@ -476,9 +471,9 @@ impl Pallet { /// Derive a (keyless) pot account from the given delegate account and account type. pub(crate) fn sub_account( account_type: AccountType, - delegate_account: T::AccountId, + delegatee_account: T::AccountId, ) -> T::AccountId { - T::PalletId::get().into_sub_account_truncating((account_type, delegate_account.clone())) + T::PalletId::get().into_sub_account_truncating((account_type, delegatee_account.clone())) } /// Balance of a delegator that is delegated. @@ -487,7 +482,7 @@ impl Pallet { } /// Returns true if who is registered as a `Delegate`. - fn is_delegate(who: &T::AccountId) -> bool { + fn is_delegatee(who: &T::AccountId) -> bool { >::contains_key(who) } @@ -517,7 +512,7 @@ impl Pallet { frame_system::Pallet::::inc_providers(who); } - fn do_migrate_to_delegate(who: &T::AccountId, reward_account: &T::AccountId) -> DispatchResult { + fn do_migrate_to_delegatee(who: &T::AccountId, reward_account: &T::AccountId) -> DispatchResult { // We create a proxy delegator that will keep all the delegation funds until funds are // transferred to actual delegator. let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, who.clone()); @@ -549,30 +544,30 @@ impl Pallet { Self::do_delegate(&proxy_delegator, who, stake.total) } - fn do_bond(delegate_acc: &T::AccountId, amount: BalanceOf) -> DispatchResult { - let delegate = Delegate::::from(delegate_acc)?; + fn do_bond(delegatee_acc: &T::AccountId, amount: BalanceOf) -> DispatchResult { + let delegatee = Delegatee::::from(delegatee_acc)?; - let available_to_bond = delegate.available_to_bond(); + let available_to_bond = delegatee.available_to_bond(); defensive_assert!(amount == available_to_bond, "not expected value to bond"); - if delegate.is_bonded() { - T::CoreStaking::bond_extra(&delegate.key, amount) + if delegatee.is_bonded() { + T::CoreStaking::bond_extra(&delegatee.key, amount) } else { - T::CoreStaking::bond(&delegate.key, amount, &delegate.reward_account()) + T::CoreStaking::bond(&delegatee.key, amount, &delegatee.reward_account()) } } fn do_delegate( delegator: &T::AccountId, - delegate: &T::AccountId, + delegatee: &T::AccountId, amount: BalanceOf, ) -> DispatchResult { - let mut ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; + let mut ledger = DelegationLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; debug_assert!(!ledger.blocked); let new_delegation_amount = if let Some(existing_delegation) = Delegation::::get(delegator) { - ensure!(&existing_delegation.delegate == delegate, Error::::InvalidDelegation); + ensure!(&existing_delegation.delegatee == delegatee, Error::::InvalidDelegation); existing_delegation .amount .checked_add(&amount) @@ -581,15 +576,15 @@ impl Pallet { amount }; - Delegation::::from(delegate, new_delegation_amount).save(delegator); + Delegation::::from(delegatee, new_delegation_amount).save(delegator); ledger.total_delegated = ledger.total_delegated.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; - ledger.save(delegate); + ledger.save(delegatee); T::Currency::hold(&HoldReason::Delegating.into(), delegator, amount)?; Self::deposit_event(Event::::Delegated { - delegate: delegate.clone(), + delegatee: delegatee.clone(), delegator: delegator.clone(), amount, }); @@ -603,24 +598,24 @@ impl Pallet { amount: BalanceOf, num_slashing_spans: u32, ) -> DispatchResult { - let mut delegate = Delegate::::from(who)?; + let mut delegatee = Delegatee::::from(who)?; let mut delegation = Delegation::::get(delegator).ok_or(Error::::NotDelegator)?; // make sure delegation to be released is sound. - ensure!(&delegation.delegate == who, Error::::NotDelegate); + ensure!(&delegation.delegatee == who, Error::::NotDelegatee); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); // if we do not already have enough funds to be claimed, try withdraw some more. - if delegate.ledger.unclaimed_withdrawals < amount { + if delegatee.ledger.unclaimed_withdrawals < amount { // get the updated delegate - delegate = Self::withdraw_unbonded(who, num_slashing_spans)?; + delegatee = Self::withdraw_unbonded(who, num_slashing_spans)?; } // if we still do not have enough funds to release, abort. - ensure!(delegate.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); + ensure!(delegatee.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); - // claim withdraw from delegate. - delegate.remove_unclaimed_withdraw(amount)?.save_or_kill()?; + // claim withdraw from delegatee. + delegatee.remove_unclaimed_withdraw(amount)?.save_or_kill()?; // book keep delegation delegation.amount = delegation @@ -642,7 +637,7 @@ impl Pallet { defensive_assert!(released == amount, "hold should have been released fully"); Self::deposit_event(Event::::Released { - delegate: who.clone(), + delegatee: who.clone(), delegator: delegator.clone(), amount, }); @@ -651,17 +646,17 @@ impl Pallet { } fn withdraw_unbonded( - delegate_acc: &T::AccountId, + delegatee_acc: &T::AccountId, num_slashing_spans: u32, - ) -> Result, DispatchError> { - let delegate = Delegate::::from(delegate_acc)?; - let pre_total = T::CoreStaking::stake(delegate_acc).defensive()?.total; + ) -> Result, DispatchError> { + let delegatee = Delegatee::::from(delegatee_acc)?; + let pre_total = T::CoreStaking::stake(delegatee_acc).defensive()?.total; let stash_killed: bool = - T::CoreStaking::withdraw_unbonded(delegate_acc.clone(), num_slashing_spans) + T::CoreStaking::withdraw_unbonded(delegatee_acc.clone(), num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; - let maybe_post_total = T::CoreStaking::stake(delegate_acc); + let maybe_post_total = T::CoreStaking::stake(delegatee_acc); // One of them should be true defensive_assert!( !(stash_killed && maybe_post_total.is_ok()), @@ -673,11 +668,11 @@ impl Pallet { let new_withdrawn = pre_total.checked_sub(&post_total).defensive_ok_or(Error::::BadState)?; - let delegate = delegate.add_unclaimed_withdraw(new_withdrawn)?; + let delegatee = delegatee.add_unclaimed_withdraw(new_withdrawn)?; - delegate.clone().save(); + delegatee.clone().save(); - Ok(delegate) + Ok(delegatee) } /// Migrates delegation of `amount` from `source` account to `destination` account. @@ -692,11 +687,11 @@ impl Pallet { // some checks that must have already been checked before. ensure!(source_delegation.amount >= amount, Error::::NotEnoughFunds); debug_assert!( - !Self::is_delegator(destination_delegator) && !Self::is_delegate(destination_delegator) + !Self::is_delegator(destination_delegator) && !Self::is_delegatee(destination_delegator) ); // update delegations - Delegation::::from(&source_delegation.delegate, amount).save(destination_delegator); + Delegation::::from(&source_delegation.delegatee, amount).save(destination_delegator); source_delegation .decrease_delegation(amount) @@ -734,15 +729,15 @@ impl Pallet { } pub fn do_slash( - delegate_acc: T::AccountId, + delegatee_acc: T::AccountId, delegator: T::AccountId, amount: BalanceOf, maybe_reporter: Option, ) -> DispatchResult { - let delegate = Delegate::::from(&delegate_acc)?; + let delegatee = Delegatee::::from(&delegatee_acc)?; let delegation = >::get(&delegator).ok_or(Error::::NotDelegator)?; - ensure!(delegation.delegate == delegate_acc, Error::::NotDelegate); + ensure!(delegation.delegatee == delegatee_acc, Error::::NotDelegatee); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); let (mut credit, missing) = @@ -752,8 +747,8 @@ impl Pallet { let actual_slash = credit.peek(); - // remove the applied slashed amount from delegate. - delegate.remove_slash(actual_slash).save(); + // remove the applied slashed amount from delegatee. + delegatee.remove_slash(actual_slash).save(); delegation .decrease_delegation(actual_slash) @@ -772,7 +767,7 @@ impl Pallet { T::OnSlash::on_unbalanced(credit); - Self::deposit_event(Event::::Slashed { delegate: delegate_acc, delegator, amount }); + Self::deposit_event(Event::::Slashed { delegatee: delegatee_acc, delegator, amount }); Ok(()) } @@ -797,10 +792,10 @@ impl Pallet { fn check_delegates( ledgers: BTreeMap>, ) -> Result<(), sp_runtime::TryRuntimeError> { - for (delegate, ledger) in ledgers { + for (delegatee, ledger) in ledgers { ensure!( matches!( - T::CoreStaking::status(&delegate).expect("delegate should be bonded"), + T::CoreStaking::status(&delegatee).expect("delegate should be bonded"), StakerStatus::Nominator(_) | StakerStatus::Idle ), "delegate should be bonded and not validator" @@ -808,7 +803,7 @@ impl Pallet { ensure!( ledger.stakeable_balance() >= - T::CoreStaking::total_stake(&delegate) + T::CoreStaking::total_stake(&delegatee) .expect("delegate should exist as a nominator"), "Cannot stake more than balance" ); @@ -827,18 +822,18 @@ impl Pallet { T::CoreStaking::status(delegator).is_err(), "delegator should not be directly staked" ); - ensure!(!Self::is_delegate(delegator), "delegator cannot be delegate"); + ensure!(!Self::is_delegatee(delegator), "delegator cannot be delegate"); delegation_aggregation - .entry(delegation.delegate.clone()) + .entry(delegation.delegatee.clone()) .and_modify(|e| *e += delegation.amount) .or_insert(delegation.amount); } - for (delegate, total_delegated) in delegation_aggregation { - ensure!(!Self::is_delegator(&delegate), "delegate cannot be delegator"); + for (delegatee, total_delegated) in delegation_aggregation { + ensure!(!Self::is_delegator(&delegatee), "delegate cannot be delegator"); - let ledger = ledger.get(&delegate).expect("ledger should exist"); + let ledger = ledger.get(&delegatee).expect("ledger should exist"); ensure!( ledger.total_delegated == total_delegated, "ledger total delegated should match delegations" diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 4d0388a7f6bc..56c8cee65d40 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{self as delegated_staking, types::Delegate, HoldReason}; +use crate::{self as delegated_staking, types::Delegatee, HoldReason}; use frame_support::{ assert_ok, derive_impl, pallet_prelude::*, @@ -283,15 +283,15 @@ pub(crate) fn fund(who: &AccountId, amount: Balance) { /// `delegate_amount` is incremented by the amount `increment` starting with `base_delegate_amount` /// from lower index to higher index of delegators. pub(crate) fn setup_delegation_stake( - delegate: AccountId, + delegatee: AccountId, reward_acc: AccountId, delegators: Vec, base_delegate_amount: Balance, increment: Balance, ) -> Balance { - fund(&delegate, 100); - assert_ok!(DelegatedStaking::register_as_delegate( - RawOrigin::Signed(delegate).into(), + fund(&delegatee, 100); + assert_ok!(DelegatedStaking::register_as_delegatee( + RawOrigin::Signed(delegatee).into(), reward_acc )); let mut delegated_amount: Balance = 0; @@ -302,14 +302,14 @@ pub(crate) fn setup_delegation_stake( fund(delegator, amount_to_delegate + ExistentialDeposit::get()); assert_ok!(DelegatedStaking::delegate_funds( RawOrigin::Signed(delegator.clone()).into(), - delegate, + delegatee, amount_to_delegate )); } // sanity checks - assert_eq!(DelegatedStaking::stakeable_balance(&delegate), delegated_amount); - assert_eq!(Delegate::::from(&delegate).unwrap().available_to_bond(), 0); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), delegated_amount); + assert_eq!(Delegatee::::from(&delegatee).unwrap().available_to_bond(), 0); delegated_amount } @@ -320,11 +320,11 @@ pub(crate) fn start_era(era: sp_staking::EraIndex) { pub(crate) fn eq_stake(who: AccountId, total: Balance, active: Balance) -> bool { Staking::stake(&who).unwrap() == Stake { total, active } && - get_delegate(&who).ledger.stakeable_balance() == total + get_delegatee(&who).ledger.stakeable_balance() == total } -pub(crate) fn get_delegate(delegate: &AccountId) -> Delegate { - Delegate::::from(delegate).expect("delegate should exist") +pub(crate) fn get_delegatee(delegatee: &AccountId) -> Delegatee { + Delegatee::::from(delegatee).expect("delegate should exist") } pub(crate) fn held_balance(who: &AccountId) -> Balance { diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index e58bec8bcf5b..6835424dde72 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -25,16 +25,16 @@ use pallet_staking::Error as StakingError; use sp_staking::delegation::StakingDelegationSupport; #[test] -fn create_a_delegate_with_first_delegator() { +fn create_a_delegatee_with_first_delegator() { ExtBuilder::default().build_and_execute(|| { - let delegate: AccountId = 200; + let delegatee: AccountId = 200; let reward_account: AccountId = 201; let delegator: AccountId = 202; // set intention to accept delegation. - fund(&delegate, 1000); - assert_ok!(DelegatedStaking::register_as_delegate( - RawOrigin::Signed(delegate).into(), + fund(&delegatee, 1000); + assert_ok!(DelegatedStaking::register_as_delegatee( + RawOrigin::Signed(delegatee).into(), reward_account )); @@ -42,45 +42,45 @@ fn create_a_delegate_with_first_delegator() { fund(&delegator, 1000); assert_ok!(DelegatedStaking::delegate_funds( RawOrigin::Signed(delegator).into(), - delegate, + delegatee, 100 )); // verify - assert!(DelegatedStaking::is_delegate(&delegate)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 100); + assert!(DelegatedStaking::is_delegatee(&delegatee)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100); assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &delegator), 100); }); } #[test] -fn cannot_become_delegate() { +fn cannot_become_delegatee() { ExtBuilder::default().build_and_execute(|| { - // cannot set reward account same as delegate account + // cannot set reward account same as delegatee account assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(100).into(), 100), + DelegatedStaking::register_as_delegatee(RawOrigin::Signed(100).into(), 100), Error::::InvalidRewardDestination ); - // an existing validator cannot become delegate + // an existing validator cannot become delegatee assert_noop!( - DelegatedStaking::register_as_delegate( + DelegatedStaking::register_as_delegatee( RawOrigin::Signed(mock::GENESIS_VALIDATOR).into(), 100 ), Error::::AlreadyStaker ); - // an existing nominator cannot become delegate + // an existing nominator cannot become delegatee assert_noop!( - DelegatedStaking::register_as_delegate( + DelegatedStaking::register_as_delegatee( RawOrigin::Signed(mock::GENESIS_NOMINATOR_ONE).into(), 100 ), Error::::AlreadyStaker ); assert_noop!( - DelegatedStaking::register_as_delegate( + DelegatedStaking::register_as_delegatee( RawOrigin::Signed(mock::GENESIS_NOMINATOR_TWO).into(), 100 ), @@ -92,17 +92,17 @@ fn cannot_become_delegate() { #[test] fn create_multiple_delegators() { ExtBuilder::default().build_and_execute(|| { - let delegate: AccountId = 200; + let delegatee: AccountId = 200; let reward_account: AccountId = 201; - // stakeable balance is 0 for non delegate - fund(&delegate, 1000); - assert!(!DelegatedStaking::is_delegate(&delegate)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); + // stakeable balance is 0 for non delegatee + fund(&delegatee, 1000); + assert!(!DelegatedStaking::is_delegatee(&delegatee)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); // set intention to accept delegation. - assert_ok!(DelegatedStaking::register_as_delegate( - RawOrigin::Signed(delegate).into(), + assert_ok!(DelegatedStaking::register_as_delegatee( + RawOrigin::Signed(delegatee).into(), reward_account )); @@ -111,65 +111,65 @@ fn create_multiple_delegators() { fund(&i, 100 + ExistentialDeposit::get()); assert_ok!(DelegatedStaking::delegate_funds( RawOrigin::Signed(i).into(), - delegate, + delegatee, 100 )); - // Balance of 100 held on delegator account for delegating to the delegate. + // Balance of 100 held on delegator account for delegating to the delegatee. assert_eq!(Balances::balance_on_hold(&HoldReason::Delegating.into(), &i), 100); } // verify - assert!(DelegatedStaking::is_delegate(&delegate)); - assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 100 * 100); + assert!(DelegatedStaking::is_delegatee(&delegatee)); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 100 * 100); }); } #[test] -fn delegate_restrictions() { +fn delegatee_restrictions() { // Similar to creating a nomination pool ExtBuilder::default().build_and_execute(|| { - let delegate_one = 200; + let delegatee_one = 200; let delegator_one = 210; - fund(&delegate_one, 100); - assert_ok!(DelegatedStaking::register_as_delegate( - RawOrigin::Signed(delegate_one).into(), - delegate_one + 1 + fund(&delegatee_one, 100); + assert_ok!(DelegatedStaking::register_as_delegatee( + RawOrigin::Signed(delegatee_one).into(), + delegatee_one + 1 )); fund(&delegator_one, 200); assert_ok!(DelegatedStaking::delegate_funds( RawOrigin::Signed(delegator_one).into(), - delegate_one, + delegatee_one, 100 )); - let delegate_two = 300; + let delegatee_two = 300; let delegator_two = 310; - fund(&delegate_two, 100); - assert_ok!(DelegatedStaking::register_as_delegate( - RawOrigin::Signed(delegate_two).into(), - delegate_two + 1 + fund(&delegatee_two, 100); + assert_ok!(DelegatedStaking::register_as_delegatee( + RawOrigin::Signed(delegatee_two).into(), + delegatee_two + 1 )); fund(&delegator_two, 200); assert_ok!(DelegatedStaking::delegate_funds( RawOrigin::Signed(delegator_two).into(), - delegate_two, + delegatee_two, 100 )); - // delegate one tries to delegate to delegate 2 + // delegatee one tries to delegate to delegatee 2 assert_noop!( DelegatedStaking::delegate_funds( - RawOrigin::Signed(delegate_one).into(), - delegate_two, + RawOrigin::Signed(delegatee_one).into(), + delegatee_two, 10 ), Error::::InvalidDelegation ); - // delegate one tries to delegate to a delegator + // delegatee one tries to delegate to a delegator assert_noop!( DelegatedStaking::delegate_funds( - RawOrigin::Signed(delegate_one).into(), + RawOrigin::Signed(delegatee_one).into(), delegator_one, 10 ), @@ -177,19 +177,19 @@ fn delegate_restrictions() { ); assert_noop!( DelegatedStaking::delegate_funds( - RawOrigin::Signed(delegate_one).into(), + RawOrigin::Signed(delegatee_one).into(), delegator_two, 10 ), Error::::InvalidDelegation ); - // delegator one tries to delegate to delegate 2 as well (it already delegates to delegate + // delegator one tries to delegate to delegatee 2 as well (it already delegates to delegatee // 1) assert_noop!( DelegatedStaking::delegate_funds( RawOrigin::Signed(delegator_one).into(), - delegate_two, + delegatee_two, 10 ), Error::::InvalidDelegation @@ -215,17 +215,17 @@ mod staking_integration { #[test] fn bond() { ExtBuilder::default().build_and_execute(|| { - let delegate: AccountId = 99; + let delegatee: AccountId = 99; let reward_acc: AccountId = 100; - assert_eq!(Staking::status(&delegate), Err(StakingError::::NotStash.into())); + assert_eq!(Staking::status(&delegatee), Err(StakingError::::NotStash.into())); - // set intention to become a delegate - fund(&delegate, 100); - assert_ok!(DelegatedStaking::register_as_delegate( - RawOrigin::Signed(delegate).into(), + // set intention to become a delegatee + fund(&delegatee, 100); + assert_ok!(DelegatedStaking::register_as_delegatee( + RawOrigin::Signed(delegatee).into(), reward_acc )); - assert_eq!(DelegatedStaking::stakeable_balance(&delegate), 0); + assert_eq!(DelegatedStaking::stakeable_balance(&delegatee), 0); let mut delegated_balance: Balance = 0; @@ -234,7 +234,7 @@ mod staking_integration { fund(&delegator, 200); assert_ok!(DelegatedStaking::delegate_funds( RawOrigin::Signed(delegator).into(), - delegate, + delegatee, 100 )); delegated_balance += 100; @@ -243,14 +243,14 @@ mod staking_integration { 100 ); - let delegate_obj = get_delegate(&delegate); - assert_eq!(delegate_obj.ledger.stakeable_balance(), delegated_balance); - assert_eq!(delegate_obj.available_to_bond(), 0); - assert_eq!(delegate_obj.bonded_stake(), delegated_balance); + let delegatee_obj = get_delegatee(&delegatee); + assert_eq!(delegatee_obj.ledger.stakeable_balance(), delegated_balance); + assert_eq!(delegatee_obj.available_to_bond(), 0); + assert_eq!(delegatee_obj.bonded_stake(), delegated_balance); } assert_eq!( - Staking::stake(&delegate).unwrap(), + Staking::stake(&delegatee).unwrap(), Stake { total: 50 * 100, active: 50 * 100 } ) }); @@ -261,89 +261,89 @@ mod staking_integration { ExtBuilder::default().build_and_execute(|| { // initial era start_era(1); - let delegate: AccountId = 200; + let delegatee: AccountId = 200; let reward_acc: AccountId = 201; let delegators: Vec = (301..=350).collect(); let total_staked = - setup_delegation_stake(delegate, reward_acc, delegators.clone(), 10, 10); + setup_delegation_stake(delegatee, reward_acc, delegators.clone(), 10, 10); // lets go to a new era start_era(2); - assert!(eq_stake(delegate, total_staked, total_staked)); + assert!(eq_stake(delegatee, total_staked, total_staked)); // Withdrawing without unbonding would fail. assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 301, 50, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 301, 50, 0), Error::::NotEnoughFunds ); - // assert_noop!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 200, 50, + // assert_noop!(DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 200, 50, // 0), Error::::NotAllowed); active and total stake remains same - assert!(eq_stake(delegate, total_staked, total_staked)); + assert!(eq_stake(delegatee, total_staked, total_staked)); // 305 wants to unbond 50 in era 2, withdrawable in era 5. - assert_ok!(DelegatedStaking::unbond(&delegate, 50)); + assert_ok!(DelegatedStaking::unbond(&delegatee, 50)); // 310 wants to unbond 100 in era 3, withdrawable in era 6. start_era(3); - assert_ok!(DelegatedStaking::unbond(&delegate, 100)); + assert_ok!(DelegatedStaking::unbond(&delegatee, 100)); // 320 wants to unbond 200 in era 4, withdrawable in era 7. start_era(4); - assert_ok!(DelegatedStaking::unbond(&delegate, 200)); + assert_ok!(DelegatedStaking::unbond(&delegatee, 200)); // active stake is now reduced.. let expected_active = total_staked - (50 + 100 + 200); - assert!(eq_stake(delegate, total_staked, expected_active)); + assert!(eq_stake(delegatee, total_staked, expected_active)); // nothing to withdraw at era 4 assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 50, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 305, 50, 0), Error::::NotEnoughFunds ); - assert!(eq_stake(delegate, total_staked, expected_active)); - assert_eq!(get_delegate(&delegate).available_to_bond(), 0); + assert!(eq_stake(delegatee, total_staked, expected_active)); + assert_eq!(get_delegatee(&delegatee).available_to_bond(), 0); // full amount is still delegated - assert_eq!(get_delegate(&delegate).ledger.effective_balance(), total_staked); + assert_eq!(get_delegatee(&delegatee).ledger.effective_balance(), total_staked); start_era(5); // at era 5, 50 tokens are withdrawable, cannot withdraw more. assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 51, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 305, 51, 0), Error::::NotEnoughFunds ); // less is possible - assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 30, 0)); - assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 20, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 305, 30, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 305, 20, 0)); // Lets go to future era where everything is unbonded. Withdrawable amount: 100 + 200 start_era(7); // 305 has no more amount delegated so it cannot withdraw. assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 305, 5, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 305, 5, 0), Error::::NotDelegator ); // 309 is an active delegator but has total delegation of 90, so it cannot withdraw more // than that. assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 309, 91, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 309, 91, 0), Error::::NotEnoughFunds ); // 310 cannot withdraw more than delegated funds. assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 310, 101, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 310, 101, 0), Error::::NotEnoughFunds ); // but can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 310, 100, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 310, 100, 0)); // 320 can withdraw all its delegation amount. - assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 320, 200, 0)); + assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 320, 200, 0)); // cannot withdraw anything more.. assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 301, 1, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 301, 1, 0), Error::::NotEnoughFunds ); assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 350, 1, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 350, 1, 0), Error::::NotEnoughFunds ); }); @@ -352,12 +352,12 @@ mod staking_integration { #[test] fn withdraw_happens_with_unbonded_balance_first() { ExtBuilder::default().build_and_execute(|| { - let delegate = 200; - setup_delegation_stake(delegate, 201, (300..350).collect(), 100, 0); + let delegatee = 200; + setup_delegation_stake(delegatee, 201, (300..350).collect(), 100, 0); // verify withdraw not possible yet assert_noop!( - DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, 0), + DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 300, 100, 0), Error::::NotEnoughFunds ); @@ -371,11 +371,11 @@ mod staking_integration { // 100)); // // // verify unbonded balance - // assert_eq!(get_delegate(&delegate).available_to_bond(), 100); + // assert_eq!(get_delegatee(&delegatee).available_to_bond(), 100); // // // withdraw works now without unbonding - // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegate).into(), 300, 100, - // 0)); assert_eq!(get_delegate(&delegate).available_to_bond(), 0); + // assert_ok!(DelegatedStaking::release(RawOrigin::Signed(delegatee).into(), 300, 100, + // 0)); assert_eq!(get_delegatee(&delegatee).available_to_bond(), 0); }); } @@ -386,14 +386,14 @@ mod staking_integration { fund(&200, 1000); let balance_200 = Balances::free_balance(200); - // `delegate` account cannot be reward destination + // `delegatee` account cannot be reward destination assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 200), + DelegatedStaking::register_as_delegatee(RawOrigin::Signed(200).into(), 200), Error::::InvalidRewardDestination ); // different reward account works - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201)); + assert_ok!(DelegatedStaking::register_as_delegatee(RawOrigin::Signed(200).into(), 201)); // add some delegations to it fund(&300, 1000); assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); @@ -419,8 +419,8 @@ mod staking_integration { // amount is staked correctly assert!(eq_stake(200, 100, 100)); - assert_eq!(get_delegate(&200).available_to_bond(), 0); - assert_eq!(get_delegate(&200).ledger.effective_balance(), 100); + assert_eq!(get_delegatee(&200).available_to_bond(), 0); + assert_eq!(get_delegatee(&200).ledger.effective_balance(), 100); // free balance of delegate is untouched assert_eq!(Balances::free_balance(200), balance_200); @@ -428,30 +428,30 @@ mod staking_integration { } #[test] - fn delegate_restrictions() { + fn delegatee_restrictions() { ExtBuilder::default().build_and_execute(|| { setup_delegation_stake(200, 201, (202..203).collect(), 100, 0); // Registering again is noop assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201), + DelegatedStaking::register_as_delegatee(RawOrigin::Signed(200).into(), 201), Error::::NotAllowed ); // a delegator cannot become delegate assert_noop!( - DelegatedStaking::register_as_delegate(RawOrigin::Signed(202).into(), 203), + DelegatedStaking::register_as_delegatee(RawOrigin::Signed(202).into(), 203), Error::::NotAllowed ); // existing staker cannot become a delegate assert_noop!( - DelegatedStaking::register_as_delegate( + DelegatedStaking::register_as_delegatee( RawOrigin::Signed(GENESIS_NOMINATOR_ONE).into(), 201 ), Error::::AlreadyStaker ); assert_noop!( - DelegatedStaking::register_as_delegate( + DelegatedStaking::register_as_delegatee( RawOrigin::Signed(GENESIS_VALIDATOR).into(), 201 ), @@ -461,16 +461,16 @@ mod staking_integration { } #[test] - fn toggle_delegate_status() { + fn toggle_delegatee_status() { ExtBuilder::default().build_and_execute(|| { - assert_ok!(DelegatedStaking::register_as_delegate(RawOrigin::Signed(200).into(), 201)); + assert_ok!(DelegatedStaking::register_as_delegatee(RawOrigin::Signed(200).into(), 201)); // delegation works fund(&300, 1000); assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); // delegate blocks delegation - assert_ok!(DelegatedStaking::toggle_delegate_status(RawOrigin::Signed(200).into())); + assert_ok!(DelegatedStaking::toggle_delegatee_status(RawOrigin::Signed(200).into())); // cannot delegate to it anymore assert_noop!( @@ -479,7 +479,7 @@ mod staking_integration { ); // delegate can unblock delegation - assert_ok!(DelegatedStaking::toggle_delegate_status(RawOrigin::Signed(200).into())); + assert_ok!(DelegatedStaking::toggle_delegatee_status(RawOrigin::Signed(200).into())); // delegation works again assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); @@ -520,7 +520,7 @@ mod staking_integration { // with at least ED. let proxy_delegator = DelegatedStaking::sub_account(AccountType::ProxyDelegator, 200); - assert_ok!(DelegatedStaking::migrate_to_delegate(RawOrigin::Signed(200).into(), 201)); + assert_ok!(DelegatedStaking::migrate_to_delegatee(RawOrigin::Signed(200).into(), 201)); // verify all went well let mut expected_proxy_delegated_amount = staked_amount; @@ -534,8 +534,8 @@ mod staking_integration { 5000 - staked_amount - ExistentialDeposit::get() ); assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); - assert_eq!(get_delegate(&200).ledger.effective_balance(), 4000); - assert_eq!(get_delegate(&200).available_to_bond(), 0); + assert_eq!(get_delegatee(&200).ledger.effective_balance(), 4000); + assert_eq!(get_delegatee(&200).available_to_bond(), 0); // now lets migrate the delegators let delegator_share = staked_amount / 4; @@ -561,8 +561,8 @@ mod staking_integration { // delegate stake is unchanged. assert_eq!(DelegatedStaking::stake(&200).unwrap(), init_stake); - assert_eq!(get_delegate(&200).ledger.effective_balance(), 4000); - assert_eq!(get_delegate(&200).available_to_bond(), 0); + assert_eq!(get_delegatee(&200).ledger.effective_balance(), 4000); + assert_eq!(get_delegatee(&200).available_to_bond(), 0); } // cannot use migrate delegator anymore @@ -602,12 +602,12 @@ mod pool_integration { assert_eq!(held_balance(&creator), delegate_amount); let pool_account = Pools::create_bonded_account(1); - let delegate = get_delegate(&pool_account); + let delegatee = get_delegatee(&pool_account); // verify state - assert_eq!(delegate.ledger.effective_balance(), delegate_amount); - assert_eq!(delegate.available_to_bond(), 0); - assert_eq!(delegate.total_unbonded(), 0); + assert_eq!(delegatee.ledger.effective_balance(), delegate_amount); + assert_eq!(delegatee.available_to_bond(), 0); + assert_eq!(delegatee.total_unbonded(), 0); }); } @@ -635,12 +635,12 @@ mod pool_integration { // delegator is not actively exposed to core staking. assert_eq!(Staking::status(&delegator), Err(StakingError::::NotStash.into())); - let pool_delegate = get_delegate(&Pools::create_bonded_account(1)); + let pool_delegatee = get_delegatee(&Pools::create_bonded_account(1)); // verify state - assert_eq!(pool_delegate.ledger.effective_balance(), staked_amount); - assert_eq!(pool_delegate.bonded_stake(), staked_amount); - assert_eq!(pool_delegate.available_to_bond(), 0); - assert_eq!(pool_delegate.total_unbonded(), 0); + assert_eq!(pool_delegatee.ledger.effective_balance(), staked_amount); + assert_eq!(pool_delegatee.bonded_stake(), staked_amount); + assert_eq!(pool_delegatee.available_to_bond(), 0); + assert_eq!(pool_delegatee.total_unbonded(), 0); // let a bunch of delegators join this pool for i in 301..350 { @@ -650,11 +650,11 @@ mod pool_integration { assert_eq!(held_balance(&i), 100 + i); } - let pool_delegate = pool_delegate.refresh().unwrap(); - assert_eq!(pool_delegate.ledger.effective_balance(), staked_amount); - assert_eq!(pool_delegate.bonded_stake(), staked_amount); - assert_eq!(pool_delegate.available_to_bond(), 0); - assert_eq!(pool_delegate.total_unbonded(), 0); + let pool_delegatee = pool_delegatee.refresh().unwrap(); + assert_eq!(pool_delegatee.ledger.effective_balance(), staked_amount); + assert_eq!(pool_delegatee.bonded_stake(), staked_amount); + assert_eq!(pool_delegatee.available_to_bond(), 0); + assert_eq!(pool_delegatee.total_unbonded(), 0); }); } @@ -664,7 +664,7 @@ mod pool_integration { let pool_id = create_pool(100, 200); add_delegators_to_pool(pool_id, (300..310).collect(), 100); let mut staked_amount = 200 + 100 * 10; - assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); + assert_eq!(get_pool_delegatee(pool_id).bonded_stake(), staked_amount); // bond extra to pool for i in 300..310 { @@ -673,7 +673,7 @@ mod pool_integration { BondExtra::FreeBalance(50) )); staked_amount += 50; - assert_eq!(get_pool_delegate(pool_id).bonded_stake(), staked_amount); + assert_eq!(get_pool_delegatee(pool_id).bonded_stake(), staked_amount); } }); } @@ -770,7 +770,7 @@ mod pool_integration { assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(301).into(), 301, 0)); assert_eq!( events_since_last_call(), - vec![Event::Released { delegate: pool_acc, delegator: 301, amount: 50 }] + vec![Event::Released { delegatee: pool_acc, delegator: 301, amount: 50 }] ); assert_eq!( pool_events_since_last_call(), @@ -787,8 +787,8 @@ mod pool_integration { assert_eq!( events_since_last_call(), vec![ - Event::Released { delegate: pool_acc, delegator: 302, amount: 100 }, - Event::Released { delegate: pool_acc, delegator: 303, amount: 200 }, + Event::Released { delegatee: pool_acc, delegator: 302, amount: 100 }, + Event::Released { delegatee: pool_acc, delegator: 303, amount: 200 }, ] ); assert_eq!( @@ -828,17 +828,17 @@ mod pool_integration { start_era(5); // withdraw pool should withdraw 1000 tokens assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).total_unbonded(), 1000); + assert_eq!(get_pool_delegatee(pool_id).total_unbonded(), 1000); start_era(6); // should withdraw 500 more assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).total_unbonded(), 1000 + 500); + assert_eq!(get_pool_delegatee(pool_id).total_unbonded(), 1000 + 500); start_era(7); // Nothing to withdraw, still at 1500. assert_ok!(Pools::pool_withdraw_unbonded(RawOrigin::Signed(100).into(), pool_id, 0)); - assert_eq!(get_pool_delegate(pool_id).total_unbonded(), 1500); + assert_eq!(get_pool_delegatee(pool_id).total_unbonded(), 1500); }); } @@ -979,12 +979,12 @@ mod pool_integration { assert_eq!(held_balance(&i), delegator_stake); } assert_eq!( - get_pool_delegate(pool_id).ledger.effective_balance(), + get_pool_delegatee(pool_id).ledger.effective_balance(), Staking::total_stake(&pool_acc).unwrap() ); // pending slash is book kept. - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); + assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, 500); // go in some distant future era. start_era(10); @@ -994,23 +994,23 @@ mod pool_integration { assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(300).into(), 300, 1)); assert_eq!( events_since_last_call(), - vec![Event::Released { delegate: pool_acc, delegator: 300, amount: 100 }] + vec![Event::Released { delegatee: pool_acc, delegator: 300, amount: 100 }] ); - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 500); + assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, 500); // withdraw the other two delegators (301 and 302) who were unbonding. for i in 301..=302 { let pre_balance = Balances::free_balance(i); - let pre_pending_slash = get_pool_delegate(pool_id).ledger.pending_slash; + let pre_pending_slash = get_pool_delegatee(pool_id).ledger.pending_slash; assert_ok!(Pools::withdraw_unbonded(RawOrigin::Signed(i).into(), i, 0)); assert_eq!( events_since_last_call(), vec![ - Event::Slashed { delegate: pool_acc, delegator: i, amount: 50 }, - Event::Released { delegate: pool_acc, delegator: i, amount: 50 }, + Event::Slashed { delegatee: pool_acc, delegator: i, amount: 50 }, + Event::Released { delegatee: pool_acc, delegator: i, amount: 50 }, ] ); - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, pre_pending_slash - 50); + assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, pre_pending_slash - 50); assert_eq!(held_balance(&i), 0); assert_eq!(Balances::free_balance(i) - pre_balance, 50); } @@ -1021,11 +1021,11 @@ mod pool_integration { fund(&slash_reporter, 100); for i in 303..306 { - let pre_pending_slash = get_pool_delegate(pool_id).ledger.pending_slash; + let pre_pending_slash = get_pool_delegatee(pool_id).ledger.pending_slash; assert_ok!(Pools::apply_slash(RawOrigin::Signed(slash_reporter).into(), i)); // each member is slashed 50% of 100 = 50. - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, pre_pending_slash - 50); + assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, pre_pending_slash - 50); // left with 50. assert_eq!(held_balance(&i), 50); } @@ -1034,7 +1034,7 @@ mod pool_integration { // slash creator assert_ok!(Pools::apply_slash(RawOrigin::Signed(slash_reporter).into(), creator)); // all slash should be applied now. - assert_eq!(get_pool_delegate(pool_id).ledger.pending_slash, 0); + assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, 0); // for creator, 50% of stake should be slashed (250), 10% of which should go to reporter // (25). assert_eq!(Balances::free_balance(slash_reporter), 115 + 25); @@ -1061,7 +1061,7 @@ mod pool_integration { } } - fn get_pool_delegate(pool_id: u32) -> Delegate { - get_delegate(&Pools::create_bonded_account(pool_id)) + fn get_pool_delegatee(pool_id: u32) -> Delegatee { + get_delegatee(&Pools::create_bonded_account(pool_id)) } } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index add4ae21847e..698a30b6b258 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -24,9 +24,9 @@ use frame_support::traits::DefensiveSaturating; /// The type of pot account being created. #[derive(Encode, Decode)] pub(crate) enum AccountType { - /// A proxy delegator account created for a nominator who migrated to a `delegate` account. + /// A proxy delegator account created for a nominator who migrated to a `delegatee` account. /// - /// Funds for unmigrated `delegator` accounts of the `delegate` are kept here. + /// Funds for unmigrated `delegator` accounts of the `delegatee` are kept here. ProxyDelegator, } @@ -35,7 +35,7 @@ pub(crate) enum AccountType { #[scale_info(skip_type_params(T))] pub struct Delegation { /// The target of delegation. - pub delegate: T::AccountId, + pub delegatee: T::AccountId, /// The amount delegated. pub amount: BalanceOf, } @@ -45,15 +45,15 @@ impl Delegation { >::get(delegator) } - pub(crate) fn from(delegate: &T::AccountId, amount: BalanceOf) -> Self { - Delegation { delegate: delegate.clone(), amount } + pub(crate) fn from(delegatee: &T::AccountId, amount: BalanceOf) -> Self { + Delegation { delegatee: delegatee.clone(), amount } } - pub(crate) fn can_delegate(delegator: &T::AccountId, delegate: &T::AccountId) -> bool { + pub(crate) fn can_delegate(delegator: &T::AccountId, delegatee: &T::AccountId) -> bool { Delegation::::get(delegator) - .map(|delegation| delegation.delegate == delegate.clone()) + .map(|delegation| delegation.delegatee == delegatee.clone()) .unwrap_or( - // all good if its a new delegator expect it should not am existing delegate. + // all good if its a new delegator except it should not be an existing delegatee. !>::contains_key(delegator), ) } @@ -61,14 +61,14 @@ impl Delegation { /// Checked decrease of delegation amount. Consumes self and returns a new copy. pub(crate) fn decrease_delegation(self, amount: BalanceOf) -> Option { let updated_delegation = self.amount.checked_sub(&amount)?; - Some(Delegation::from(&self.delegate, updated_delegation)) + Some(Delegation::from(&self.delegatee, updated_delegation)) } /// Checked increase of delegation amount. Consumes self and returns a new copy. #[allow(unused)] pub(crate) fn increase_delegation(self, amount: BalanceOf) -> Option { let updated_delegation = self.amount.checked_add(&amount)?; - Some(Delegation::from(&self.delegate, updated_delegation)) + Some(Delegation::from(&self.delegatee, updated_delegation)) } pub(crate) fn save(self, key: &T::AccountId) { @@ -84,8 +84,8 @@ impl Delegation { /// Ledger of all delegations to a `Delegate`. /// -/// This keeps track of the active balance of the `delegate` that is made up from the funds that are -/// currently delegated to this `delegate`. It also tracks the pending slashes yet to be applied +/// This keeps track of the active balance of the `delegatee` that is made up from the funds that are +/// currently delegated to this `delegatee`. It also tracks the pending slashes yet to be applied /// among other things. // FIXME(ank4n): Break up into two storage items - bookkeeping stuff and settings stuff. #[derive(Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -93,7 +93,7 @@ impl Delegation { pub struct DelegationLedger { /// Where the reward should be paid out. pub payee: T::AccountId, - /// Sum of all delegated funds to this `delegate`. + /// Sum of all delegated funds to this `delegatee`. #[codec(compact)] pub total_delegated: BalanceOf, /// Funds that are withdrawn from core staking but not released to delegator/s. It is a subset @@ -104,7 +104,7 @@ pub struct DelegationLedger { /// Slashes that are not yet applied. #[codec(compact)] pub pending_slash: BalanceOf, - /// Whether this `delegate` is blocked from receiving new delegations. + /// Whether this `delegatee` is blocked from receiving new delegations. pub blocked: bool, } @@ -123,8 +123,8 @@ impl DelegationLedger { >::get(key) } - pub(crate) fn can_accept_delegation(delegate: &T::AccountId) -> bool { - DelegationLedger::::get(delegate) + pub(crate) fn can_accept_delegation(delegatee: &T::AccountId) -> bool { + DelegationLedger::::get(delegatee) .map(|ledger| !ledger.blocked) .unwrap_or(false) } @@ -133,7 +133,7 @@ impl DelegationLedger { >::insert(key, self) } - /// Effective total balance of the `delegate`. + /// Effective total balance of the `delegatee`. pub(crate) fn effective_balance(&self) -> BalanceOf { defensive_assert!( self.total_delegated >= self.pending_slash, @@ -152,15 +152,15 @@ impl DelegationLedger { /// Wrapper around `DelegationLedger` to provide additional functionality. #[derive(Clone)] -pub struct Delegate { +pub struct Delegatee { pub key: T::AccountId, pub ledger: DelegationLedger, } -impl Delegate { - pub(crate) fn from(delegate: &T::AccountId) -> Result, DispatchError> { - let ledger = DelegationLedger::::get(delegate).ok_or(Error::::NotDelegate)?; - Ok(Delegate { key: delegate.clone(), ledger }) +impl Delegatee { + pub(crate) fn from(delegatee: &T::AccountId) -> Result, DispatchError> { + let ledger = DelegationLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; + Ok(Delegatee { key: delegatee.clone(), ledger }) } /// Remove funds that are withdrawn from [Config::CoreStaking] but not claimed by a delegator. @@ -182,7 +182,7 @@ impl Delegate { .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - Ok(Delegate { + Ok(Delegatee { ledger: DelegationLedger { total_delegated: new_total_delegated, unclaimed_withdrawals: new_unclaimed_withdrawals, @@ -203,7 +203,7 @@ impl Delegate { .checked_add(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - Ok(Delegate { + Ok(Delegatee { ledger: DelegationLedger { unclaimed_withdrawals: new_unclaimed_withdrawals, ..self.ledger @@ -214,7 +214,7 @@ impl Delegate { /// Reloads self from storage. #[allow(unused)] - pub(crate) fn refresh(&self) -> Result, DispatchError> { + pub(crate) fn refresh(&self) -> Result, DispatchError> { Self::from(&self.key) } @@ -249,7 +249,7 @@ impl Delegate { let pending_slash = self.ledger.pending_slash.defensive_saturating_sub(amount); let total_delegated = self.ledger.total_delegated.defensive_saturating_sub(amount); - Delegate { + Delegatee { ledger: DelegationLedger { pending_slash, total_delegated, ..self.ledger }, ..self } @@ -268,7 +268,7 @@ impl Delegate { } pub(crate) fn update_status(self, block: bool) -> Self { - Delegate { ledger: DelegationLedger { blocked: block, ..self.ledger }, ..self } + Delegatee { ledger: DelegationLedger { blocked: block, ..self.ledger }, ..self } } pub(crate) fn save(self) { diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 33387817b95c..28dcec8d11f2 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1107,7 +1107,7 @@ impl Pallet { } pub(crate) fn stakeable_balance(who: &T::AccountId) -> BalanceOf { - if T::DelegationSupport::is_delegate(who) { + if T::DelegationSupport::is_delegatee(who) { return T::DelegationSupport::stakeable_balance(who); } @@ -1118,7 +1118,7 @@ impl Pallet { who: &T::AccountId, reward_destination: Option, ) -> bool { - if T::DelegationSupport::is_delegate(who) { + if T::DelegationSupport::is_delegatee(who) { return T::DelegationSupport::restrict_reward_destination(who, reward_destination); } @@ -1129,8 +1129,8 @@ impl Pallet { who: &T::AccountId, amount: BalanceOf, ) -> sp_runtime::DispatchResult { - // only apply lock if it is not a delegate. Delegate accounts are already locked/held. - if !T::DelegationSupport::is_delegate(who) { + // only apply lock if it is not a delegatee. delegatee accounts are already locked/held. + if !T::DelegationSupport::is_delegatee(who) { T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); } @@ -1892,12 +1892,12 @@ impl StakingInterface for Pallet { delegatee: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - Err(DispatchError::Other("delegate is not supported")) + Err(DispatchError::Other("delegate_extra is not supported")) } /// Withdraw delegation from pool account to self. fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { - Err(DispatchError::Other("delegate is not supported")) + Err(DispatchError::Other("withdraw_delegation is not supported")) } /// Does the delegatee have any pending slash. @@ -1907,7 +1907,7 @@ impl StakingInterface for Pallet { } fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { - Err(DispatchError::Other("delegate is not supported")) + Err(DispatchError::Other("delegator_slash is not supported")) } fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { @@ -1926,7 +1926,7 @@ impl StakingDelegationSupport for NoDelegation { defensive!("stakeable balance should not have been called for NoDelegation"); BalanceOf::::zero() } - fn is_delegate(_who: &Self::AccountId) -> bool { + fn is_delegatee(_who: &Self::AccountId) -> bool { false } fn report_slash(_who: &Self::AccountId, _slash: Self::Balance) { diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index 00cdf64f5d4a..dcc9b9fe440b 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -608,7 +608,7 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; - let lazy_slash = T::DelegationSupport::is_delegate(stash); + let lazy_slash = T::DelegationSupport::is_delegatee(stash); let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); if value.is_zero() { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 0f18f32f3ccc..e7bfb55a27e4 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -49,8 +49,8 @@ pub trait StakingDelegationSupport { } /// Returns true if `who` accepts delegations for stake. - fn is_delegate(who: &Self::AccountId) -> bool; + fn is_delegatee(who: &Self::AccountId) -> bool; - /// Reports an ongoing slash to the `delegate` account that would be applied lazily. + /// Reports an ongoing slash to the `delegatee` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } \ No newline at end of file From b68bb9d848689eb2977562f7da2345208fbcb124 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 03:19:03 +0100 Subject: [PATCH 175/202] fmt --- .../frame/delegated-staking/src/impls.rs | 22 +++++++++++++++---- substrate/frame/delegated-staking/src/lib.rs | 17 ++++++++------ .../frame/delegated-staking/src/tests.rs | 10 +++++++-- .../frame/delegated-staking/src/types.rs | 6 ++--- substrate/frame/nomination-pools/src/lib.rs | 7 ++++-- substrate/frame/nomination-pools/src/mock.rs | 21 ++++++++++++++---- substrate/frame/staking/src/pallet/impls.rs | 20 ++++++++++++++--- .../primitives/staking/src/delegation.rs | 2 +- substrate/primitives/staking/src/lib.rs | 21 ++++++++++++++---- 9 files changed, 96 insertions(+), 30 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 585378e9c74e..e2d4fe99f1c7 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -206,7 +206,12 @@ impl StakingInterface for Pallet { } /// Delegate funds to `Delegatee`. - fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn delegate( + who: &Self::AccountId, + delegatee: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { Pallet::::register_as_delegatee( RawOrigin::Signed(delegatee.clone()).into(), reward_account.clone(), @@ -234,7 +239,11 @@ impl StakingInterface for Pallet { } /// Withdraw delegation from pool account to self. - fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn withdraw_delegation( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { // fixme(ank4n): This should not require slashing spans. Pallet::::release(RawOrigin::Signed(delegatee.clone()).into(), who.clone(), amount, 0) } @@ -246,7 +255,12 @@ impl StakingInterface for Pallet { .unwrap_or(false) } - fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { + fn delegator_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> sp_runtime::DispatchResult { Pallet::::do_slash(delegatee.clone(), delegator.clone(), value, maybe_reporter) } @@ -302,4 +316,4 @@ impl StakingDelegationSupport for Pallet { }, }); } -} \ No newline at end of file +} diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 8890b2f261d0..e86792d35e54 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -55,11 +55,11 @@ //! //! #### Dispatchable Calls //! The pallet exposes the following [`Call`]s: -//! - `register_as_delegatee`: Register an account to be a `delegatee`. Once an account is registered -//! as a `delegatee`, for staking operations, only its delegated funds are used. This means it -//! cannot use its own free balance to stake. +//! - `register_as_delegatee`: Register an account to be a `delegatee`. Once an account is +//! registered as a `delegatee`, for staking operations, only its delegated funds are used. This +//! means it cannot use its own free balance to stake. //! - `migrate_to_delegate`: This allows a `Nominator` account to become a `delegatee` account. -//! Explained in more detail in the `Migration` section. +//! Explained in more detail in the `Migration` section. //! - `release`: Release funds to `delegator` from `unclaimed_withdrawals` register of the //! `delegatee`. //! - `migrate_delegation`: Migrate delegated funds from one account to another. This is useful for @@ -512,7 +512,10 @@ impl Pallet { frame_system::Pallet::::inc_providers(who); } - fn do_migrate_to_delegatee(who: &T::AccountId, reward_account: &T::AccountId) -> DispatchResult { + fn do_migrate_to_delegatee( + who: &T::AccountId, + reward_account: &T::AccountId, + ) -> DispatchResult { // We create a proxy delegator that will keep all the delegation funds until funds are // transferred to actual delegator. let proxy_delegator = Self::sub_account(AccountType::ProxyDelegator, who.clone()); @@ -626,7 +629,6 @@ impl Pallet { // remove delegator if nothing delegated anymore delegation.save(delegator); - let released = T::Currency::release( &HoldReason::Delegating.into(), &delegator, @@ -687,7 +689,8 @@ impl Pallet { // some checks that must have already been checked before. ensure!(source_delegation.amount >= amount, Error::::NotEnoughFunds); debug_assert!( - !Self::is_delegator(destination_delegator) && !Self::is_delegatee(destination_delegator) + !Self::is_delegator(destination_delegator) && + !Self::is_delegatee(destination_delegator) ); // update delegations diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 6835424dde72..0f30918caf18 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -1010,7 +1010,10 @@ mod pool_integration { Event::Released { delegatee: pool_acc, delegator: i, amount: 50 }, ] ); - assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, pre_pending_slash - 50); + assert_eq!( + get_pool_delegatee(pool_id).ledger.pending_slash, + pre_pending_slash - 50 + ); assert_eq!(held_balance(&i), 0); assert_eq!(Balances::free_balance(i) - pre_balance, 50); } @@ -1025,7 +1028,10 @@ mod pool_integration { assert_ok!(Pools::apply_slash(RawOrigin::Signed(slash_reporter).into(), i)); // each member is slashed 50% of 100 = 50. - assert_eq!(get_pool_delegatee(pool_id).ledger.pending_slash, pre_pending_slash - 50); + assert_eq!( + get_pool_delegatee(pool_id).ledger.pending_slash, + pre_pending_slash - 50 + ); // left with 50. assert_eq!(held_balance(&i), 50); } diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 698a30b6b258..e31d84b6943b 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -84,9 +84,9 @@ impl Delegation { /// Ledger of all delegations to a `Delegate`. /// -/// This keeps track of the active balance of the `delegatee` that is made up from the funds that are -/// currently delegated to this `delegatee`. It also tracks the pending slashes yet to be applied -/// among other things. +/// This keeps track of the active balance of the `delegatee` that is made up from the funds that +/// are currently delegated to this `delegatee`. It also tracks the pending slashes yet to be +/// applied among other things. // FIXME(ank4n): Break up into two storage items - bookkeeping stuff and settings stuff. #[derive(Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 10e6fc69a04c..327b4aa6260b 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -351,6 +351,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use adapter::StakeAdapter; use codec::Codec; use frame_support::{ defensive, defensive_assert, ensure, @@ -375,7 +376,6 @@ use sp_runtime::{ }; use sp_staking::{EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; -use adapter::StakeAdapter; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] use sp_runtime::TryRuntimeError; @@ -1655,7 +1655,10 @@ pub mod pallet { type MaxMetadataLen: Get; /// An adapter to support delegated to direct staking. - type StakeAdapter: adapter::StakeAdapter>; + type StakeAdapter: adapter::StakeAdapter< + AccountId = Self::AccountId, + Balance = BalanceOf, + >; } /// The sum of funds across all pools. diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index be4c644beea3..7fec33043fbd 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -239,7 +239,12 @@ impl sp_staking::StakingInterface for StakingMock { } /// Delegate funds to `Delegatee`. - fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn delegate( + who: &Self::AccountId, + delegatee: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { unimplemented!("method currently not used in testing") } @@ -253,7 +258,11 @@ impl sp_staking::StakingInterface for StakingMock { } /// Withdraw delegation from pool account to self. - fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn withdraw_delegation( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { unimplemented!("method currently not used in testing") } @@ -262,14 +271,18 @@ impl sp_staking::StakingInterface for StakingMock { false } - fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { + fn delegator_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> sp_runtime::DispatchResult { unimplemented!("method currently not used in testing") } fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { unimplemented!("method currently not used in testing") } - } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 28dcec8d11f2..8fb22fc13f57 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1882,7 +1882,12 @@ impl StakingInterface for Pallet { } /// Delegate funds to `Delegatee`. - fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn delegate( + who: &Self::AccountId, + delegatee: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { Err(DispatchError::Other("delegate is not supported")) } @@ -1896,7 +1901,11 @@ impl StakingInterface for Pallet { } /// Withdraw delegation from pool account to self. - fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult { + fn withdraw_delegation( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult { Err(DispatchError::Other("withdraw_delegation is not supported")) } @@ -1906,7 +1915,12 @@ impl StakingInterface for Pallet { false } - fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult { + fn delegator_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> sp_runtime::DispatchResult { Err(DispatchError::Other("delegator_slash is not supported")) } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index e7bfb55a27e4..d7b8fc1e1246 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -53,4 +53,4 @@ pub trait StakingDelegationSupport { /// Reports an ongoing slash to the `delegatee` account that would be applied lazily. fn report_slash(who: &Self::AccountId, slash: Self::Balance); -} \ No newline at end of file +} diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 0e1384442106..0ed417155f05 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -319,7 +319,12 @@ pub trait StakingInterface { fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; /// Delegate funds to `Delegatee`. - fn delegate(who: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + fn delegate( + who: &Self::AccountId, + delegatee: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; /// Add more delegation to the pool account. fn delegate_extra( @@ -329,15 +334,23 @@ pub trait StakingInterface { ) -> DispatchResult; /// Withdraw delegation from pool account to self. - fn withdraw_delegation(who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance) -> DispatchResult; + fn withdraw_delegation( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; /// Does the delegatee have any pending slash. fn has_pending_slash(delegatee: &Self::AccountId) -> bool; - fn delegator_slash(delegatee: &Self::AccountId, delegator: &Self::AccountId, value: Self::Balance, maybe_reporter: Option) -> sp_runtime::DispatchResult; + fn delegator_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> sp_runtime::DispatchResult; fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; - } /// The amount of exposure for an era that an individual nominator has (susceptible to slashing). From a34094995f4abb81f83ea54a4ee95cefe2570370 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 03:24:58 +0100 Subject: [PATCH 176/202] name changes --- .../frame/nomination-pools/src/adapter.rs | 22 +++++++++---------- substrate/frame/nomination-pools/src/lib.rs | 6 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 0bf042fd47e7..5b098e20ad1c 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -33,22 +33,22 @@ pub trait StakeAdapter { fn total_balance(who: &Self::AccountId) -> Self::Balance; /// Bond delegator via the pool account. - fn bond( + fn delegator_bond( who: &Self::AccountId, pool_account: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - /// Add more bond via the pool account. - fn bond_extra( + /// Add more bond for delegator via the pool account. + fn delegator_bond_extra( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - /// Claim withdrawn amount in the pool account. - fn claim_withdraw( + /// Withdrawn amount from pool to delegator. + fn delegator_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -77,7 +77,7 @@ impl StakeAdapter for TransferStake { T::Currency::total_balance(who) } - fn bond( + fn delegator_bond( who: &Self::AccountId, pool_account: &Self::AccountId, reward_account: &Self::AccountId, @@ -87,7 +87,7 @@ impl StakeAdapter for TransferStake { T::Staking::bond(pool_account, amount, reward_account) } - fn bond_extra( + fn delegator_bond_extra( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -96,7 +96,7 @@ impl StakeAdapter for TransferStake { T::Staking::bond_extra(pool_account, amount) } - fn claim_withdraw( + fn delegator_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -135,7 +135,7 @@ impl StakeAdapter for DelegationStake { T::Currency::total_balance(who) } - fn bond( + fn delegator_bond( who: &Self::AccountId, pool_account: &Self::AccountId, reward_account: &Self::AccountId, @@ -145,7 +145,7 @@ impl StakeAdapter for DelegationStake { T::Staking::delegate(who, pool_account, reward_account, amount) } - fn bond_extra( + fn delegator_bond_extra( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, @@ -153,7 +153,7 @@ impl StakeAdapter for DelegationStake { T::Staking::delegate_extra(who, pool_account, amount) } - fn claim_withdraw( + fn delegator_withdraw( who: &Self::AccountId, pool_account: &Self::AccountId, amount: Self::Balance, diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 327b4aa6260b..f76a1fb9ed3a 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1265,11 +1265,11 @@ impl BondedPool { match ty { BondType::Create => - T::StakeAdapter::bond(who, &bonded_account, &reward_account, amount)?, + T::StakeAdapter::delegator_bond(who, &bonded_account, &reward_account, amount)?, // The pool should always be created in such a way its in a state to bond extra, but if // the active balance is slashed below the minimum bonded or the account cannot be // found, we exit early. - BondType::Later => T::StakeAdapter::bond_extra(who, &bonded_account, amount)?, + BondType::Later => T::StakeAdapter::delegator_bond_extra(who, &bonded_account, amount)?, } TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); @@ -2303,7 +2303,7 @@ pub mod pallet { // order to ensure members can leave the pool and it can be destroyed. .min(bonded_pool.transferable_balance()); - T::StakeAdapter::claim_withdraw( + T::StakeAdapter::delegator_withdraw( &member_account, &bonded_pool.bonded_account(), balance_to_unbond, From de8f87542aa417a8d5369392ffdd2cf696b0f3d0 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 16:29:21 +0100 Subject: [PATCH 177/202] doc update --- .../frame/delegated-staking/src/impls.rs | 4 +-- substrate/frame/delegated-staking/src/lib.rs | 4 +-- substrate/frame/delegated-staking/src/mock.rs | 4 +-- .../frame/delegated-staking/src/tests.rs | 2 +- .../frame/nomination-pools/src/adapter.rs | 19 +++++++------- .../nomination-pools/test-staking/src/mock.rs | 2 +- substrate/frame/staking/src/mock.rs | 2 +- substrate/frame/staking/src/pallet/impls.rs | 26 +++++++++---------- substrate/frame/staking/src/pallet/mod.rs | 8 +++--- substrate/frame/staking/src/slashing.rs | 6 ++--- .../primitives/staking/src/delegation.rs | 20 +++++++++----- 11 files changed, 53 insertions(+), 44 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index e2d4fe99f1c7..028ac34465d5 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Implementations of public traits, namely [StakingInterface], and [StakingDelegationSupport]. +//! Implementations of public traits, namely [StakingInterface], and [DelegateeSupport]. use super::*; @@ -269,7 +269,7 @@ impl StakingInterface for Pallet { } } -impl StakingDelegationSupport for Pallet { +impl DelegateeSupport for Pallet { type Balance = BalanceOf; type AccountId = T::AccountId; diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index e86792d35e54..bb2dac70eb4e 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -74,7 +74,7 @@ //! [Config::CoreStaking] to provide delegation based staking. NominationPool can use this pallet as //! its Staking provider to support delegation based staking from pool accounts. //! -//! #### [Staking Delegation Support](StakingDelegationSupport) +//! #### [Staking Delegation Support](DelegateeSupport) //! The pallet implements the staking delegation support trait which staking pallet can use to //! provide compatibility with this pallet. //! @@ -171,7 +171,7 @@ use sp_runtime::{ ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; use sp_staking::{ - delegation::StakingDelegationSupport, EraIndex, Stake, StakerStatus, StakingInterface, + delegation::DelegateeSupport, EraIndex, Stake, StakerStatus, StakingInterface, }; use sp_std::{convert::TryInto, prelude::*}; diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 56c8cee65d40..756b43316e17 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -34,7 +34,7 @@ use frame_support::dispatch::RawOrigin; use pallet_staking::CurrentEra; use sp_core::U256; use sp_runtime::traits::Convert; -use sp_staking::{delegation::StakingDelegationSupport, Stake, StakingInterface}; +use sp_staking::{delegation::DelegateeSupport, Stake, StakingInterface}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -109,7 +109,7 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; - type DelegationSupport = DelegatedStaking; + type DelegateeSupport = DelegatedStaking; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 0f30918caf18..0dfdfd574650 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -22,7 +22,7 @@ use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use pallet_nomination_pools::{Error as PoolsError, Event as PoolsEvent}; use pallet_staking::Error as StakingError; -use sp_staking::delegation::StakingDelegationSupport; +use sp_staking::delegation::DelegateeSupport; #[test] fn create_a_delegatee_with_first_delegator() { diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 5b098e20ad1c..6f42514493b2 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -17,11 +17,10 @@ use crate::*; -/// An adapter trait that can support multiple staking strategies: e.g. `Transfer and stake` or -/// `delegation and stake`. +/// An adapter trait that can support multiple staking strategies. /// -/// This trait is very specific to nomination pool and meant to make switching between different -/// staking implementations easier. +/// Depending on which staking strategy we want to use, the staking logic can be slightly +/// different. Refer the two possible strategies currently: [`TransferStake`] and [`DelegationStake`]. pub trait StakeAdapter { type Balance: frame_support::traits::tokens::Balance; type AccountId: Clone + sp_std::fmt::Debug; @@ -55,9 +54,10 @@ pub trait StakeAdapter { ) -> DispatchResult; } -/// An adapter implementation that supports transfer based staking +/// An adapter implementation that supports transfer based staking. /// -/// The funds are transferred to the pool account and then staked via the pool account. +/// In order to stake, this adapter transfers the funds from the delegator account to the pool +/// account and stakes directly on [Config::Staking]. pub struct TransferStake(PhantomData); impl StakeAdapter for TransferStake { @@ -107,10 +107,11 @@ impl StakeAdapter for TransferStake { } } -/// An adapter implementation that supports delegation based staking +/// An adapter implementation that supports delegation based staking. /// -/// The funds are delegated from pool account to a delegatee and then staked. The advantage of this -/// approach is that the funds are held in the delegator account and not in the pool account. +/// In this approach, first the funds are delegated from delegator to the pool account and later +/// staked with [Config::Staking]. The advantage of this approach is that the funds are held in the +/// use account itself and not in the pool account. pub struct DelegationStake(PhantomData); impl StakeAdapter for DelegationStake { diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index c8446a6e077a..54dfe198c660 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -111,7 +111,7 @@ parameter_types! { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; - type DelegationSupport = pallet_staking::NoDelegation; + type DelegateeSupport = pallet_staking::NoDelegation; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 725e5d753bdc..44d171a1806c 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -266,7 +266,7 @@ impl OnStakingUpdate for EventListenerMock { impl crate::pallet::pallet::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; - type DelegationSupport = pallet_staking::NoDelegation; + type DelegateeSupport = pallet_staking::NoDelegation; type UnixTime = Timestamp; type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 8fb22fc13f57..ecddb1b34e67 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -39,12 +39,12 @@ use sp_runtime::{ Perbill, Percent, }; use sp_staking::{ - currency_to_vote::CurrencyToVote, - delegation::StakingDelegationSupport, - offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, - StakingAccount::{self, Controller, Stash}, - StakingInterface, + currency_to_vote::CurrencyToVote, + delegation::DelegateeSupport, + offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, + EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, + StakingAccount::{self, Controller, Stash}, + StakingInterface, }; use sp_std::prelude::*; @@ -1107,8 +1107,8 @@ impl Pallet { } pub(crate) fn stakeable_balance(who: &T::AccountId) -> BalanceOf { - if T::DelegationSupport::is_delegatee(who) { - return T::DelegationSupport::stakeable_balance(who); + if T::DelegateeSupport::is_delegatee(who) { + return T::DelegateeSupport::stakeable_balance(who); } T::Currency::free_balance(who) @@ -1118,8 +1118,8 @@ impl Pallet { who: &T::AccountId, reward_destination: Option, ) -> bool { - if T::DelegationSupport::is_delegatee(who) { - return T::DelegationSupport::restrict_reward_destination(who, reward_destination); + if T::DelegateeSupport::is_delegatee(who) { + return T::DelegateeSupport::restrict_reward_destination(who, reward_destination); } false @@ -1130,7 +1130,7 @@ impl Pallet { amount: BalanceOf, ) -> sp_runtime::DispatchResult { // only apply lock if it is not a delegatee. delegatee accounts are already locked/held. - if !T::DelegationSupport::is_delegatee(who) { + if !T::DelegateeSupport::is_delegatee(who) { T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); } @@ -1930,10 +1930,10 @@ impl StakingInterface for Pallet { } } -/// Standard implementation of `StakingDelegationSupport` that supports only direct staking and no +/// Standard implementation of `DelegateeSupport` that supports only direct staking and no /// delegated staking. pub struct NoDelegation(PhantomData); -impl StakingDelegationSupport for NoDelegation { +impl DelegateeSupport for NoDelegation { type Balance = BalanceOf; type AccountId = T::AccountId; fn stakeable_balance(_who: &Self::AccountId) -> Self::Balance { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 27fdb320b5ba..861eda301646 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,9 +37,9 @@ use sp_runtime::{ }; use sp_staking::{ - delegation::StakingDelegationSupport, - EraIndex, Page, SessionIndex, - StakingAccount::{self, Controller, Stash}, + delegation::DelegateeSupport, + EraIndex, Page, SessionIndex, + StakingAccount::{self, Controller, Stash}, }; use sp_std::prelude::*; @@ -106,7 +106,7 @@ pub mod pallet { + MaxEncodedLen; /// Something that provides delegation support to staking pallet. - type DelegationSupport: StakingDelegationSupport< + type DelegateeSupport: DelegateeSupport< Balance = Self::CurrencyBalance, AccountId = Self::AccountId, >; diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index dcc9b9fe440b..364dc4215dfc 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -64,7 +64,7 @@ use sp_runtime::{ traits::{Saturating, Zero}, DispatchResult, RuntimeDebug, }; -use sp_staking::{delegation::StakingDelegationSupport, offence::DisableStrategy, EraIndex}; +use sp_staking::{delegation::DelegateeSupport, offence::DisableStrategy, EraIndex}; use sp_std::vec::Vec; /// The proportion of the slashing reward to be paid out on the first slashing detection. @@ -608,7 +608,7 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; - let lazy_slash = T::DelegationSupport::is_delegatee(stash); + let lazy_slash = T::DelegateeSupport::is_delegatee(stash); let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); if value.is_zero() { @@ -618,7 +618,7 @@ pub fn do_slash( if lazy_slash { // If delegated staking, report slash and move on. - T::DelegationSupport::report_slash(stash, value); + T::DelegateeSupport::report_slash(stash, value); } else { let (imbalance, missing) = T::Currency::slash(stash, value); slashed_imbalance.subsume(imbalance); diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index d7b8fc1e1246..ede3f77cce8c 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -20,8 +20,12 @@ use scale_info::TypeInfo; use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; -/// Something that provides delegation support to core staking. -pub trait StakingDelegationSupport { +/// A trait that can be used as a plugin to support delegation based accounts, called `Delegatee`. +/// +/// For example, `pallet-staking` which implements `StakingInterface` but does not implement +/// account delegations out of the box can be provided with a custom implementation of this trait to +/// learn how to handle these special accounts. +pub trait DelegateeSupport { /// Balance type used by the staking system. type Balance: Sub + Ord @@ -36,10 +40,11 @@ pub trait StakingDelegationSupport { /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; - /// Balance of who which is available for stake. - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance; + /// Balance of `delegatee` which is available for stake. + fn stakeable_balance(delegatee: &Self::AccountId) -> Self::Balance; - /// Returns true if provided reward destination is not allowed. + /// Returns true if `delegatee` is restricted to update which account they can receive their + /// staking rewards. fn restrict_reward_destination( _who: &Self::AccountId, _reward_destination: Option, @@ -48,9 +53,12 @@ pub trait StakingDelegationSupport { false } - /// Returns true if `who` accepts delegations for stake. + /// Returns true if `who` is a `delegatee` and accepts delegations from other accounts. fn is_delegatee(who: &Self::AccountId) -> bool; /// Reports an ongoing slash to the `delegatee` account that would be applied lazily. + /// + /// Slashing a delegatee account is not immediate since the balance is made up of multiple child + /// delegators. This function should bookkeep the slash to be applied later. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } From fd27cd5044970153b294852afd7e2e0cfa630a4b Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 16:55:42 +0100 Subject: [PATCH 178/202] rename to delegatee (missed ones) --- substrate/frame/delegated-staking/src/lib.rs | 52 ++++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index bb2dac70eb4e..f846292695b9 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -74,9 +74,9 @@ //! [Config::CoreStaking] to provide delegation based staking. NominationPool can use this pallet as //! its Staking provider to support delegation based staking from pool accounts. //! -//! #### [Staking Delegation Support](DelegateeSupport) -//! The pallet implements the staking delegation support trait which staking pallet can use to -//! provide compatibility with this pallet. +//! #### [Delegatee Support](DelegateeSupport) +//! Implements `DelegateeSupport` trait which another pallet such as [Config::CoreStaking] can use +//! to back-support `Delegatees` in their staking implementation. //! //! ## Lazy Slashing //! One of the reasons why direct nominators on staking pallet cannot scale well is because all @@ -92,7 +92,7 @@ //! `NominationPool` can apply slash for all its members by calling //! [StakingInterface::delegator_slash](sp_staking::StakingInterface::delegator_slash). //! -//! ## Migration from Nominator to Delegate +//! ## Migration from Nominator to Delegatee //! More details [here](https://hackmd.io/@ak0n/np-delegated-staking-migration). //! //! ## Reward Destination Restrictions @@ -118,7 +118,7 @@ //! //! #### Nomination Pool with delegation support //! 1) delegate fund from delegator to pool account, and -//! 2) stake from pool account as a `Delegate` account on the staking pallet. +//! 2) stake from pool account as a `Delegatee` account on the staking pallet. //! //! The difference being, in the second approach, the delegated funds will be locked in-place in //! user's account enabling them to participate in use cases that allows use of `held` funds such @@ -270,16 +270,16 @@ pub mod pallet { pub(crate) type Delegators = CountedStorageMap<_, Twox64Concat, T::AccountId, Delegation, OptionQuery>; - /// Map of `Delegate` to their `DelegationLedger`. + /// Map of `Delegatee` to their `DelegationLedger`. #[pallet::storage] pub(crate) type Delegates = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; #[pallet::call] impl Pallet { - /// Register an account to be a `Delegate`. + /// Register an account to be a `Delegatee`. /// - /// `Delegate` accounts accepts delegations from other `delegator`s and stake funds on their + /// `Delegatee` accounts accepts delegations from other `delegator`s and stake funds on their /// behalf. #[pallet::call_index(0)] #[pallet::weight(Weight::default())] @@ -305,17 +305,17 @@ pub mod pallet { Ok(()) } - /// Migrate from a `Nominator` account to `Delegate` account. + /// Migrate from a `Nominator` account to `Delegatee` account. /// /// The origin needs to /// - be a `Nominator` with `CoreStaking`, - /// - not already a `Delegate`, + /// - not already a `Delegatee`, /// - have enough funds to transfer existential deposit to a delegator account created for /// the migration. /// /// This operation will create a new delegator account for the origin called /// `proxy_delegator` and transfer the staked amount to it. The `proxy_delegator` delegates - /// the funds to the origin making origin a `Delegate` account. The actual `delegator` + /// the funds to the origin making origin a `Delegatee` account. The actual `delegator` /// accounts of the origin can later migrate their funds using [Call::migrate_delegation] to /// claim back their share of delegated funds from `proxy_delegator` to self. #[pallet::call_index(1)] @@ -377,7 +377,7 @@ pub mod pallet { ensure!(!Self::is_delegator(&delegator), Error::::NotAllowed); ensure!(Self::not_direct_staker(&delegator), Error::::AlreadyStaker); - // ensure delegate is sane. + // ensure delegatee is sane. ensure!(Self::is_delegatee(&delegatee), Error::::NotDelegatee); // and has enough delegated balance to migrate. @@ -388,7 +388,7 @@ pub mod pallet { Self::do_migrate_delegation(&proxy_delegator, &delegator, amount) } - /// Delegate funds to a `Delegate` account and bonds it to [Config::CoreStaking]. + /// Delegate funds to a `Delegatee` account and bonds it to [Config::CoreStaking]. /// /// If delegation already exists, it increases the delegation by `amount`. #[pallet::call_index(4)] @@ -407,7 +407,7 @@ pub mod pallet { ensure!(Delegation::::can_delegate(&who, &delegatee), Error::::InvalidDelegation); ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); - // ensure delegate is sane. + // ensure delegatee is sane. ensure!( DelegationLedger::::can_accept_delegation(&delegatee), Error::::NotAcceptingDelegations @@ -423,9 +423,9 @@ pub mod pallet { Self::do_bond(&delegatee, amount) } - /// Toggle delegate status to start or stop accepting new delegations. + /// Toggle delegatee status to start or stop accepting new delegations. /// - /// This can only be used by existing delegates. If not a delegate yet, use + /// This can only be used by existing delegates. If not a delegatee yet, use /// [Call::register_as_delegatee] first. #[pallet::call_index(5)] #[pallet::weight(Weight::default())] @@ -441,7 +441,7 @@ pub mod pallet { /// Apply slash to a delegator account. /// - /// `Delegate` accounts with pending slash in their ledger can call this to apply slash to + /// `Delegatee` accounts with pending slash in their ledger can call this to apply slash to /// one of its `delegator` account. Each slash to a delegator account needs to be posted /// separately until all pending slash is cleared. #[pallet::call_index(6)] @@ -468,7 +468,7 @@ pub mod pallet { } impl Pallet { - /// Derive a (keyless) pot account from the given delegate account and account type. + /// Derive a (keyless) pot account from the given delegatee account and account type. pub(crate) fn sub_account( account_type: AccountType, delegatee_account: T::AccountId, @@ -481,12 +481,12 @@ impl Pallet { T::Currency::balance_on_hold(&HoldReason::Delegating.into(), who) } - /// Returns true if who is registered as a `Delegate`. + /// Returns true if who is registered as a `Delegatee`. fn is_delegatee(who: &T::AccountId) -> bool { >::contains_key(who) } - /// Returns true if who is delegating to a `Delegate` account. + /// Returns true if who is delegating to a `Delegatee` account. fn is_delegator(who: &T::AccountId) -> bool { >::contains_key(who) } @@ -610,7 +610,7 @@ impl Pallet { // if we do not already have enough funds to be claimed, try withdraw some more. if delegatee.ledger.unclaimed_withdrawals < amount { - // get the updated delegate + // get the updated delegatee delegatee = Self::withdraw_unbonded(who, num_slashing_spans)?; } @@ -798,16 +798,16 @@ impl Pallet { for (delegatee, ledger) in ledgers { ensure!( matches!( - T::CoreStaking::status(&delegatee).expect("delegate should be bonded"), + T::CoreStaking::status(&delegatee).expect("delegatee should be bonded"), StakerStatus::Nominator(_) | StakerStatus::Idle ), - "delegate should be bonded and not validator" + "delegatee should be bonded and not validator" ); ensure!( ledger.stakeable_balance() >= T::CoreStaking::total_stake(&delegatee) - .expect("delegate should exist as a nominator"), + .expect("delegatee should exist as a nominator"), "Cannot stake more than balance" ); } @@ -825,7 +825,7 @@ impl Pallet { T::CoreStaking::status(delegator).is_err(), "delegator should not be directly staked" ); - ensure!(!Self::is_delegatee(delegator), "delegator cannot be delegate"); + ensure!(!Self::is_delegatee(delegator), "delegator cannot be delegatee"); delegation_aggregation .entry(delegation.delegatee.clone()) @@ -834,7 +834,7 @@ impl Pallet { } for (delegatee, total_delegated) in delegation_aggregation { - ensure!(!Self::is_delegator(&delegatee), "delegate cannot be delegator"); + ensure!(!Self::is_delegator(&delegatee), "delegatee cannot be delegator"); let ledger = ledger.get(&delegatee).expect("ledger should exist"); ensure!( From 1f120323c8d12ec9b5c8ff7ea0b1eb8342096027 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 17:09:04 +0100 Subject: [PATCH 179/202] minor refactors --- .../frame/delegated-staking/src/impls.rs | 6 ++-- substrate/frame/delegated-staking/src/lib.rs | 24 ++++++------- .../frame/delegated-staking/src/tests.rs | 2 +- .../frame/delegated-staking/src/types.rs | 22 ++++++------ substrate/frame/staking/src/pallet/impls.rs | 36 +++++++++---------- .../primitives/staking/src/delegation.rs | 2 +- 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 028ac34465d5..356d59473842 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -109,7 +109,7 @@ impl StakingInterface for Pallet { } fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { - let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; + let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; ensure!(delegation_register.stakeable_balance() >= extra, Error::::NotEnoughFunds); T::CoreStaking::bond_extra(who, extra) @@ -285,7 +285,7 @@ impl DelegateeSupport for Pallet { who: &Self::AccountId, reward_destination: Option, ) -> bool { - let maybe_register = >::get(who); + let maybe_register = >::get(who); if maybe_register.is_none() { // no restrictions for non delegates. @@ -309,7 +309,7 @@ impl DelegateeSupport for Pallet { } fn report_slash(who: &Self::AccountId, slash: Self::Balance) { - >::mutate(who, |maybe_register| match maybe_register { + >::mutate(who, |maybe_register| match maybe_register { Some(register) => register.pending_slash.saturating_accrue(slash), None => { defensive!("should not be called on non-delegate"); diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index f846292695b9..9af6593cf8af 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -272,7 +272,7 @@ pub mod pallet { /// Map of `Delegatee` to their `DelegationLedger`. #[pallet::storage] - pub(crate) type Delegates = + pub(crate) type Delegatees = CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; #[pallet::call] @@ -301,7 +301,7 @@ pub mod pallet { // Reward account cannot be same as `delegatee` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); - Self::do_register_delegator(&who, &reward_account); + Self::do_register_delegatee(&who, &reward_account); Ok(()) } @@ -483,7 +483,7 @@ impl Pallet { /// Returns true if who is registered as a `Delegatee`. fn is_delegatee(who: &T::AccountId) -> bool { - >::contains_key(who) + >::contains_key(who) } /// Returns true if who is delegating to a `Delegatee` account. @@ -503,10 +503,10 @@ impl Pallet { .unwrap_or(false) } - fn do_register_delegator(who: &T::AccountId, reward_account: &T::AccountId) { + fn do_register_delegatee(who: &T::AccountId, reward_account: &T::AccountId) { DelegationLedger::::new(reward_account).save(who); - // Pool account is a virtual account. Make this account exist. + // Delegatee is a virtual account. Make this account exist. // TODO: Someday if we expose these calls in a runtime, we should take a deposit for // being a delegator. frame_system::Pallet::::inc_providers(who); @@ -540,7 +540,7 @@ impl Pallet { T::Currency::transfer(who, &proxy_delegator, stake.total, Preservation::Protect) .map_err(|_| Error::::BadState)?; - Self::do_register_delegator(who, reward_account); + Self::do_register_delegatee(who, reward_account); // FIXME(ank4n) expose set payee in staking interface. // T::CoreStaking::set_payee(who, reward_account) @@ -579,7 +579,7 @@ impl Pallet { amount }; - Delegation::::from(delegatee, new_delegation_amount).save(delegator); + Delegation::::from(delegatee, new_delegation_amount).save_or_kill(delegator); ledger.total_delegated = ledger.total_delegated.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; ledger.save(delegatee); @@ -627,7 +627,7 @@ impl Pallet { .defensive_ok_or(ArithmeticError::Overflow)?; // remove delegator if nothing delegated anymore - delegation.save(delegator); + delegation.save_or_kill(delegator); let released = T::Currency::release( &HoldReason::Delegating.into(), @@ -694,12 +694,12 @@ impl Pallet { ); // update delegations - Delegation::::from(&source_delegation.delegatee, amount).save(destination_delegator); + Delegation::::from(&source_delegation.delegatee, amount).save_or_kill(destination_delegator); source_delegation .decrease_delegation(amount) .defensive_ok_or(Error::::BadState)? - .save(source_delegator); + .save_or_kill(source_delegator); // FIXME(ank4n): If all funds are migrated from source, it can be cleaned up and ED returned // to delegate or alternatively whoever cleans it up. This could be a permission-less @@ -756,7 +756,7 @@ impl Pallet { delegation .decrease_delegation(actual_slash) .ok_or(ArithmeticError::Overflow)? - .save(&delegator); + .save_or_kill(&delegator); if let Some(reporter) = maybe_reporter { let reward_payout: BalanceOf = @@ -784,7 +784,7 @@ impl Pallet { pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { // build map to avoid reading storage multiple times. let delegation_map = Delegators::::iter().collect::>(); - let ledger_map = Delegates::::iter().collect::>(); + let ledger_map = Delegatees::::iter().collect::>(); Self::check_delegates(ledger_map.clone())?; Self::check_delegators(delegation_map, ledger_map)?; diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 0dfdfd574650..9d29727d70d2 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -914,7 +914,7 @@ mod pool_integration { ); // Make sure all data is cleaned up. - assert_eq!(Delegates::::contains_key(Pools::create_bonded_account(pool_id)), false); + assert_eq!(Delegatees::::contains_key(Pools::create_bonded_account(pool_id)), false); assert_eq!(Delegators::::contains_key(creator), false); for i in 300..310 { assert_eq!(Delegators::::contains_key(i), false); diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index e31d84b6943b..4edb4051bf3a 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -54,7 +54,7 @@ impl Delegation { .map(|delegation| delegation.delegatee == delegatee.clone()) .unwrap_or( // all good if its a new delegator except it should not be an existing delegatee. - !>::contains_key(delegator), + !>::contains_key(delegator), ) } @@ -71,7 +71,7 @@ impl Delegation { Some(Delegation::from(&self.delegatee, updated_delegation)) } - pub(crate) fn save(self, key: &T::AccountId) { + pub(crate) fn save_or_kill(self, key: &T::AccountId) { // Clean up if no delegation left. if self.amount == Zero::zero() { >::remove(key); @@ -82,7 +82,7 @@ impl Delegation { } } -/// Ledger of all delegations to a `Delegate`. +/// Ledger of all delegations to a `Delegatee`. /// /// This keeps track of the active balance of the `delegatee` that is made up from the funds that /// are currently delegated to this `delegatee`. It also tracks the pending slashes yet to be @@ -120,7 +120,7 @@ impl DelegationLedger { } pub(crate) fn get(key: &T::AccountId) -> Option { - >::get(key) + >::get(key) } pub(crate) fn can_accept_delegation(delegatee: &T::AccountId) -> bool { @@ -130,7 +130,7 @@ impl DelegationLedger { } pub(crate) fn save(self, key: &T::AccountId) { - >::insert(key, self) + >::insert(key, self) } /// Effective total balance of the `delegatee`. @@ -226,20 +226,22 @@ impl Delegatee { let bonded_stake = self.bonded_stake(); let stakeable = self.ledger.stakeable_balance(); - defensive_assert!(stakeable >= bonded_stake, "cannot expose more than delegate balance"); + defensive_assert!(stakeable >= bonded_stake, "cannot be bonded with more than delegatee balance"); stakeable.saturating_sub(bonded_stake) } - /// Balance of `Delegate` that is not bonded. + /// Balance of `Delegatee` that is not bonded. /// - /// Includes `unclaimed_withdrawals` of `Delegate`. + /// This is similar to [Self::available_to_bond] except it also includes `unclaimed_withdrawals` + /// of `Delegatee`. + #[cfg(test)] pub(crate) fn total_unbonded(&self) -> BalanceOf { let bonded_stake = self.bonded_stake(); let net_balance = self.ledger.effective_balance(); - defensive_assert!(net_balance >= bonded_stake, "cannot expose more than delegate balance"); + defensive_assert!(net_balance >= bonded_stake, "cannot be bonded with more than the delegatee balance"); net_balance.saturating_sub(bonded_stake) } @@ -288,7 +290,7 @@ impl Delegatee { self.ledger.pending_slash == Zero::zero(), Error::::BadState ); - >::remove(key); + >::remove(key); } else { self.ledger.save(&key) } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index ecddb1b34e67..bd3af7d103de 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1871,60 +1871,60 @@ impl StakingInterface for Pallet { T::Currency::remove_lock(crate::STAKING_ID, who) } - fn is_delegatee(who: &Self::AccountId) -> bool { + fn is_delegatee(_who: &Self::AccountId) -> bool { defensive!("is_delegatee is not supported"); false } - fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { + fn delegatee_balance(_who: &Self::AccountId) -> Self::Balance { defensive!("delegatee_balance is not supported"); BalanceOf::::zero() } /// Delegate funds to `Delegatee`. fn delegate( - who: &Self::AccountId, - delegatee: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, + _who: &Self::AccountId, + _delegatee: &Self::AccountId, + _reward_account: &Self::AccountId, + _amount: Self::Balance, ) -> DispatchResult { Err(DispatchError::Other("delegate is not supported")) } /// Add more delegation to the pool account. fn delegate_extra( - who: &Self::AccountId, - delegatee: &Self::AccountId, - amount: Self::Balance, + _who: &Self::AccountId, + _delegatee: &Self::AccountId, + _amount: Self::Balance, ) -> DispatchResult { Err(DispatchError::Other("delegate_extra is not supported")) } /// Withdraw delegation from pool account to self. fn withdraw_delegation( - who: &Self::AccountId, - delegatee: &Self::AccountId, - amount: Self::Balance, + _who: &Self::AccountId, + _delegatee: &Self::AccountId, + _amount: Self::Balance, ) -> DispatchResult { Err(DispatchError::Other("withdraw_delegation is not supported")) } /// Does the delegatee have any pending slash. - fn has_pending_slash(delegatee: &Self::AccountId) -> bool { + fn has_pending_slash(_delegatee: &Self::AccountId) -> bool { // slashing is greedy, so no pending slash. false } fn delegator_slash( - delegatee: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, + _delegatee: &Self::AccountId, + _delegator: &Self::AccountId, + _value: Self::Balance, + _maybe_reporter: Option, ) -> sp_runtime::DispatchResult { Err(DispatchError::Other("delegator_slash is not supported")) } - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { + fn delegated_balance(_delegator: &Self::AccountId) -> Self::Balance { defensive!("delegated_balance is not supported"); BalanceOf::::zero() } diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index ede3f77cce8c..c8a9f366d701 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -17,7 +17,7 @@ use codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime::{DispatchResult, Saturating}; +use sp_runtime::Saturating; use sp_std::ops::Sub; /// A trait that can be used as a plugin to support delegation based accounts, called `Delegatee`. From 9c2bb9eb508574085c3c1ec3386bf820a000b504 Mon Sep 17 00:00:00 2001 From: Ankan Date: Tue, 27 Feb 2024 17:18:19 +0100 Subject: [PATCH 180/202] doc updates --- .../frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 18 +++++++-------- .../frame/delegated-staking/src/types.rs | 22 +++++++++---------- substrate/primitives/staking/src/lib.rs | 15 ++++++++----- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 356d59473842..438425e01b9b 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -124,7 +124,7 @@ impl StakingInterface for Pallet { /// Withdraw unbonding funds until current era. /// - /// Funds are moved to unclaimed_withdrawals register of the `DelegationLedger`. + /// Funds are moved to unclaimed_withdrawals register of the `DelegateeLedger`. fn withdraw_unbonded( pool_acc: Self::AccountId, num_slashing_spans: u32, diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 9af6593cf8af..89360ad56d10 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -46,7 +46,7 @@ //! ## Key Terminologies //! - *delegatee*: An account who accepts delegations from other accounts (called `Delegators`). //! - *Delegator*: An account who delegates their funds to a `delegatee`. -//! - *DelegationLedger*: A data structure that stores important information about the `delegatee` +//! - *DelegateeLedger*: A data structure that stores important information about the `delegatee` //! such as their total delegated stake. //! - *Delegation*: A data structure that stores the amount of funds delegated to a `delegatee` by a //! `delegator`. @@ -83,7 +83,7 @@ //! nominators are slashed at the same time. This is expensive and needs to be bounded operation. //! //! This pallet implements a lazy slashing mechanism. Any slashes to a `delegatee` are posted in its -//! `DelegationLedger` as a pending slash. Since the actual amount is held in the multiple +//! `DelegateeLedger` as a pending slash. Since the actual amount is held in the multiple //! `delegator` accounts, this pallet has no way to know how to apply slash. It is `delegatee`'s //! responsibility to apply slashes for each delegator, one at a time. Staking pallet ensures the //! pending slash never exceeds staked amount and would freeze further withdraws until pending @@ -270,10 +270,10 @@ pub mod pallet { pub(crate) type Delegators = CountedStorageMap<_, Twox64Concat, T::AccountId, Delegation, OptionQuery>; - /// Map of `Delegatee` to their `DelegationLedger`. + /// Map of `Delegatee` to their `DelegateeLedger`. #[pallet::storage] pub(crate) type Delegatees = - CountedStorageMap<_, Twox64Concat, T::AccountId, DelegationLedger, OptionQuery>; + CountedStorageMap<_, Twox64Concat, T::AccountId, DelegateeLedger, OptionQuery>; #[pallet::call] impl Pallet { @@ -409,7 +409,7 @@ pub mod pallet { // ensure delegatee is sane. ensure!( - DelegationLedger::::can_accept_delegation(&delegatee), + DelegateeLedger::::can_accept_delegation(&delegatee), Error::::NotAcceptingDelegations ); @@ -504,7 +504,7 @@ impl Pallet { } fn do_register_delegatee(who: &T::AccountId, reward_account: &T::AccountId) { - DelegationLedger::::new(reward_account).save(who); + DelegateeLedger::::new(reward_account).save(who); // Delegatee is a virtual account. Make this account exist. // TODO: Someday if we expose these calls in a runtime, we should take a deposit for @@ -565,7 +565,7 @@ impl Pallet { delegatee: &T::AccountId, amount: BalanceOf, ) -> DispatchResult { - let mut ledger = DelegationLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; + let mut ledger = DelegateeLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; debug_assert!(!ledger.blocked); let new_delegation_amount = @@ -793,7 +793,7 @@ impl Pallet { } fn check_delegates( - ledgers: BTreeMap>, + ledgers: BTreeMap>, ) -> Result<(), sp_runtime::TryRuntimeError> { for (delegatee, ledger) in ledgers { ensure!( @@ -817,7 +817,7 @@ impl Pallet { fn check_delegators( delegations: BTreeMap>, - ledger: BTreeMap>, + ledger: BTreeMap>, ) -> Result<(), sp_runtime::TryRuntimeError> { let mut delegation_aggregation = BTreeMap::>::new(); for (delegator, delegation) in delegations.iter() { diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 4edb4051bf3a..4356122f8b66 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -90,7 +90,7 @@ impl Delegation { // FIXME(ank4n): Break up into two storage items - bookkeeping stuff and settings stuff. #[derive(Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] -pub struct DelegationLedger { +pub struct DelegateeLedger { /// Where the reward should be paid out. pub payee: T::AccountId, /// Sum of all delegated funds to this `delegatee`. @@ -108,9 +108,9 @@ pub struct DelegationLedger { pub blocked: bool, } -impl DelegationLedger { +impl DelegateeLedger { pub(crate) fn new(reward_destination: &T::AccountId) -> Self { - DelegationLedger { + DelegateeLedger { payee: reward_destination.clone(), total_delegated: Zero::zero(), unclaimed_withdrawals: Zero::zero(), @@ -124,7 +124,7 @@ impl DelegationLedger { } pub(crate) fn can_accept_delegation(delegatee: &T::AccountId) -> bool { - DelegationLedger::::get(delegatee) + DelegateeLedger::::get(delegatee) .map(|ledger| !ledger.blocked) .unwrap_or(false) } @@ -150,16 +150,16 @@ impl DelegationLedger { } } -/// Wrapper around `DelegationLedger` to provide additional functionality. +/// Wrapper around `DelegateeLedger` to provide additional functionality. #[derive(Clone)] pub struct Delegatee { pub key: T::AccountId, - pub ledger: DelegationLedger, + pub ledger: DelegateeLedger, } impl Delegatee { pub(crate) fn from(delegatee: &T::AccountId) -> Result, DispatchError> { - let ledger = DelegationLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; + let ledger = DelegateeLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; Ok(Delegatee { key: delegatee.clone(), ledger }) } @@ -183,7 +183,7 @@ impl Delegatee { .defensive_ok_or(ArithmeticError::Overflow)?; Ok(Delegatee { - ledger: DelegationLedger { + ledger: DelegateeLedger { total_delegated: new_total_delegated, unclaimed_withdrawals: new_unclaimed_withdrawals, ..self.ledger @@ -204,7 +204,7 @@ impl Delegatee { .defensive_ok_or(ArithmeticError::Overflow)?; Ok(Delegatee { - ledger: DelegationLedger { + ledger: DelegateeLedger { unclaimed_withdrawals: new_unclaimed_withdrawals, ..self.ledger }, @@ -252,7 +252,7 @@ impl Delegatee { let total_delegated = self.ledger.total_delegated.defensive_saturating_sub(amount); Delegatee { - ledger: DelegationLedger { pending_slash, total_delegated, ..self.ledger }, + ledger: DelegateeLedger { pending_slash, total_delegated, ..self.ledger }, ..self } } @@ -270,7 +270,7 @@ impl Delegatee { } pub(crate) fn update_status(self, block: bool) -> Self { - Delegatee { ledger: DelegationLedger { blocked: block, ..self.ledger }, ..self } + Delegatee { ledger: DelegateeLedger { blocked: block, ..self.ledger }, ..self } } pub(crate) fn save(self) { diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 0ed417155f05..5131d0d9dc36 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -320,29 +320,32 @@ pub trait StakingInterface { /// Delegate funds to `Delegatee`. fn delegate( - who: &Self::AccountId, + delegator: &Self::AccountId, delegatee: &Self::AccountId, reward_account: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - /// Add more delegation to the pool account. + /// Add more delegation to the `delegatee`. fn delegate_extra( - who: &Self::AccountId, + delegator: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - /// Withdraw delegation from pool account to self. + /// Withdraw or revoke delegation to `delegatee`. fn withdraw_delegation( who: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; - /// Does the delegatee have any pending slash. + /// Returns true if there are pending slashes posted to the `delegatee` account. fn has_pending_slash(delegatee: &Self::AccountId) -> bool; + /// Apply a pending slash to a `delegatee` by slashing `value` from `delegator`. + /// + /// If a reporter is provided, the reporter will receive a fraction of the slash as reward. fn delegator_slash( delegatee: &Self::AccountId, delegator: &Self::AccountId, @@ -350,6 +353,8 @@ pub trait StakingInterface { maybe_reporter: Option, ) -> sp_runtime::DispatchResult; + + /// Returns the total amount of funds delegated by a `delegator`. fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; } From 244df2275e520cadf1095bd34872c3cdad3ffc71 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:16:25 +0100 Subject: [PATCH 181/202] Stake Adapter refactor (#3502) --- .../frame/delegated-staking/src/impls.rs | 3 + substrate/frame/delegated-staking/src/lib.rs | 11 +- substrate/frame/delegated-staking/src/mock.rs | 3 +- .../frame/delegated-staking/src/types.rs | 10 +- .../frame/nomination-pools/src/adapter.rs | 301 +++++++++++++----- substrate/frame/nomination-pools/src/lib.rs | 105 +++--- .../frame/nomination-pools/src/migration.rs | 5 +- substrate/frame/nomination-pools/src/mock.rs | 58 +--- .../nomination-pools/test-staking/src/mock.rs | 3 +- substrate/frame/staking/src/pallet/impls.rs | 70 +--- substrate/frame/staking/src/pallet/mod.rs | 6 +- .../primitives/staking/src/delegation.rs | 49 ++- substrate/primitives/staking/src/lib.rs | 47 --- 13 files changed, 335 insertions(+), 336 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 438425e01b9b..67ff007b6d27 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -19,6 +19,7 @@ //! Implementations of public traits, namely [StakingInterface], and [DelegateeSupport]. use super::*; +use sp_staking::delegation::DelegatedStakeInterface; /// StakingInterface implementation with delegation support. /// @@ -193,7 +194,9 @@ impl StakingInterface for Pallet { fn set_current_era(era: EraIndex) { T::CoreStaking::set_current_era(era) } +} +impl DelegatedStakeInterface for Pallet { fn is_delegatee(who: &Self::AccountId) -> bool { Self::is_delegatee(who) } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 89360ad56d10..5b180f3e0b21 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -170,9 +170,7 @@ use sp_runtime::{ traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; -use sp_staking::{ - delegation::DelegateeSupport, EraIndex, Stake, StakerStatus, StakingInterface, -}; +use sp_staking::{delegation::DelegateeSupport, EraIndex, Stake, StakerStatus, StakingInterface}; use sp_std::{convert::TryInto, prelude::*}; pub type BalanceOf = @@ -279,8 +277,8 @@ pub mod pallet { impl Pallet { /// Register an account to be a `Delegatee`. /// - /// `Delegatee` accounts accepts delegations from other `delegator`s and stake funds on their - /// behalf. + /// `Delegatee` accounts accepts delegations from other `delegator`s and stake funds on + /// their behalf. #[pallet::call_index(0)] #[pallet::weight(Weight::default())] pub fn register_as_delegatee( @@ -694,7 +692,8 @@ impl Pallet { ); // update delegations - Delegation::::from(&source_delegation.delegatee, amount).save_or_kill(destination_delegator); + Delegation::::from(&source_delegation.delegatee, amount) + .save_or_kill(destination_delegator); source_delegation .decrease_delegation(amount) diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 756b43316e17..892b69b624e4 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -175,13 +175,12 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = sp_runtime::FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type Staking = DelegatedStaking; type PostUnbondingPoolsWindow = ConstU32<2>; type PalletId = PoolsPalletId; type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type StakeAdapter = pallet_nomination_pools::adapter::DelegationStake; + type StakeAdapter = pallet_nomination_pools::adapter::DelegateStake; } frame_support::construct_runtime!( diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 4356122f8b66..ec16c3da3987 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -226,7 +226,10 @@ impl Delegatee { let bonded_stake = self.bonded_stake(); let stakeable = self.ledger.stakeable_balance(); - defensive_assert!(stakeable >= bonded_stake, "cannot be bonded with more than delegatee balance"); + defensive_assert!( + stakeable >= bonded_stake, + "cannot be bonded with more than delegatee balance" + ); stakeable.saturating_sub(bonded_stake) } @@ -241,7 +244,10 @@ impl Delegatee { let net_balance = self.ledger.effective_balance(); - defensive_assert!(net_balance >= bonded_stake, "cannot be bonded with more than the delegatee balance"); + defensive_assert!( + net_balance >= bonded_stake, + "cannot be bonded with more than the delegatee balance" + ); net_balance.saturating_sub(bonded_stake) } diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 6f42514493b2..6546ef5f38d7 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -16,149 +16,286 @@ // limitations under the License. use crate::*; +use sp_staking::delegation::DelegatedStakeInterface; /// An adapter trait that can support multiple staking strategies. /// /// Depending on which staking strategy we want to use, the staking logic can be slightly -/// different. Refer the two possible strategies currently: [`TransferStake`] and [`DelegationStake`]. -pub trait StakeAdapter { +/// different. Refer the two possible strategies currently: [`TransferStake`] and +/// [`DelegateStake`]. +pub trait StakeStrategy { type Balance: frame_support::traits::tokens::Balance; type AccountId: Clone + sp_std::fmt::Debug; - /// Balance of the account. - fn balance(who: &Self::AccountId) -> Self::Balance; + fn bonding_duration() -> EraIndex; + fn current_era() -> EraIndex; + fn minimum_nominator_bond() -> Self::Balance; - /// Total balance of the account. - fn total_balance(who: &Self::AccountId) -> Self::Balance; + /// Transferable balance of the pool. + /// + /// This is the amount that can be withdrawn from the pool. + /// + /// Does not include reward account. + fn transferable_balance(id: PoolId) -> Self::Balance; - /// Bond delegator via the pool account. - fn delegator_bond( + /// Total balance of the pool including amount that is actively staked. + fn total_balance(id: PoolId) -> Self::Balance; + fn member_delegation_balance(member_account: &Self::AccountId) -> Self::Balance; + + fn active_stake(pool: PoolId) -> Self::Balance; + fn total_stake(pool: PoolId) -> Self::Balance; + + fn nominate(pool_id: PoolId, validators: Vec) -> DispatchResult; + + fn chill(pool_id: PoolId) -> DispatchResult; + + fn bond( who: &Self::AccountId, - pool_account: &Self::AccountId, - reward_account: &Self::AccountId, + pool_id: PoolId, amount: Self::Balance, + bond_type: BondType, ) -> DispatchResult; - /// Add more bond for delegator via the pool account. - fn delegator_bond_extra( + fn unbond(pool_id: PoolId, amount: Self::Balance) -> DispatchResult; + + fn withdraw_unbonded(pool_id: PoolId, num_slashing_spans: u32) -> Result; + + fn member_withdraw( who: &Self::AccountId, - pool_account: &Self::AccountId, + pool: PoolId, amount: Self::Balance, ) -> DispatchResult; - /// Withdrawn amount from pool to delegator. - fn delegator_withdraw( + fn has_pending_slash(pool: PoolId) -> bool; + + fn member_slash( who: &Self::AccountId, - pool_account: &Self::AccountId, + pool: PoolId, amount: Self::Balance, + maybe_reporter: Option, ) -> DispatchResult; } -/// An adapter implementation that supports transfer based staking. +/// A staking strategy implementation that supports transfer based staking. /// /// In order to stake, this adapter transfers the funds from the delegator account to the pool -/// account and stakes directly on [Config::Staking]. -pub struct TransferStake(PhantomData); +/// account and stakes directly on `Staking`. +pub struct TransferStake(PhantomData<(T, Staking)>); -impl StakeAdapter for TransferStake { +impl, AccountId = T::AccountId>> + StakeStrategy for TransferStake +{ type Balance = BalanceOf; type AccountId = T::AccountId; - fn balance(who: &Self::AccountId) -> Self::Balance { - // Note on why we can't use `Currency::reducible_balance`: Since pooled account has a - // provider (staking pallet), the account can not be set expendable by - // `pallet-nomination-pool`. This means reducible balance always returns balance preserving - // ED in the account. What we want though is transferable balance given the account can be - // dusted. - T::Currency::balance(who) + fn bonding_duration() -> EraIndex { + Staking::bonding_duration() + } + fn current_era() -> EraIndex { + Staking::current_era() + } + fn minimum_nominator_bond() -> Staking::Balance { + Staking::minimum_nominator_bond() } - fn total_balance(who: &Self::AccountId) -> Self::Balance { - T::Currency::total_balance(who) + fn transferable_balance(pool: PoolId) -> BalanceOf { + let pool_account = Pallet::::create_bonded_account(pool); + T::Currency::balance(&pool_account).saturating_sub(Self::active_stake(pool)) } - fn delegator_bond( - who: &Self::AccountId, - pool_account: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - T::Currency::transfer(who, &pool_account, amount, Preservation::Expendable)?; - T::Staking::bond(pool_account, amount, reward_account) + fn total_balance(pool: PoolId) -> BalanceOf { + let pool_account = Pallet::::create_bonded_account(pool); + T::Currency::total_balance(&pool_account) } - fn delegator_bond_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - T::Currency::transfer(who, pool_account, amount, Preservation::Preserve)?; - T::Staking::bond_extra(pool_account, amount) + fn member_delegation_balance(_member_account: &T::AccountId) -> Staking::Balance { + defensive!("delegation not supported"); + Zero::zero() } - fn delegator_withdraw( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, + fn active_stake(pool: PoolId) -> BalanceOf { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::active_stake(&pool_account).unwrap_or_default() + } + + fn total_stake(pool: PoolId) -> Staking::Balance { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::total_stake(&pool_account).unwrap_or_default() + } + + fn nominate(pool_id: PoolId, validators: Vec) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::nominate(&pool_account, validators) + } + + fn chill(pool_id: PoolId) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::chill(&pool_account) + } + + fn bond( + who: &T::AccountId, + pool: PoolId, + amount: BalanceOf, + bond_type: BondType, ) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool); + let reward_account = Pallet::::create_reward_account(pool); + + match bond_type { + BondType::Create => { + // first bond + T::Currency::transfer(who, &pool_account, amount, Preservation::Expendable)?; + Staking::bond(&pool_account, amount, &reward_account) + }, + BondType::Later => { + // additional bond + T::Currency::transfer(who, &pool_account, amount, Preservation::Preserve)?; + Staking::bond_extra(&pool_account, amount) + }, + } + } + + fn unbond(pool_id: PoolId, amount: Staking::Balance) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::unbond(&pool_account, amount) + } + + fn withdraw_unbonded(pool_id: PoolId, num_slashing_spans: u32) -> Result { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::withdraw_unbonded(pool_account, num_slashing_spans) + } + + fn member_withdraw(who: &T::AccountId, pool: PoolId, amount: BalanceOf) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool); T::Currency::transfer(&pool_account, &who, amount, Preservation::Expendable)?; Ok(()) } + + fn has_pending_slash(_pool: PoolId) -> bool { + // for transfer stake strategy, slashing is greedy + false + } + + fn member_slash( + _who: &T::AccountId, + _pool: PoolId, + _amount: Staking::Balance, + _maybe_reporter: Option, + ) -> DispatchResult { + Err(Error::::Defensive(DefensiveError::DelegationUnsupported).into()) + } } -/// An adapter implementation that supports delegation based staking. +/// A staking strategy implementation that supports delegation based staking. /// /// In this approach, first the funds are delegated from delegator to the pool account and later -/// staked with [Config::Staking]. The advantage of this approach is that the funds are held in the +/// staked with `Staking`. The advantage of this approach is that the funds are held in the /// use account itself and not in the pool account. -pub struct DelegationStake(PhantomData); +pub struct DelegateStake(PhantomData<(T, Staking)>); -impl StakeAdapter for DelegationStake { +impl< + T: Config, + Staking: DelegatedStakeInterface, AccountId = T::AccountId>, + > StakeStrategy for DelegateStake +{ type Balance = BalanceOf; type AccountId = T::AccountId; - fn balance(who: &Self::AccountId) -> Self::Balance { - // Pool account is a delegatee, and its balance is the sum of all member delegations towards - // it. - if T::Staking::is_delegatee(who) { - return T::Staking::delegatee_balance(who); - } + fn bonding_duration() -> EraIndex { + Staking::bonding_duration() + } + fn current_era() -> EraIndex { + Staking::current_era() + } + fn minimum_nominator_bond() -> Staking::Balance { + Staking::minimum_nominator_bond() + } - T::Currency::balance(who) + fn transferable_balance(pool: PoolId) -> BalanceOf { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::delegatee_balance(&pool_account).saturating_sub(Self::active_stake(pool)) } - fn total_balance(who: &Self::AccountId) -> Self::Balance { - if T::Staking::is_delegatee(who) { - return T::Staking::delegatee_balance(who); - } + fn total_balance(pool: PoolId) -> BalanceOf { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::delegatee_balance(&pool_account) + } - T::Currency::total_balance(who) + fn member_delegation_balance(member_account: &T::AccountId) -> Staking::Balance { + Staking::delegated_balance(member_account) } - fn delegator_bond( - who: &Self::AccountId, - pool_account: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - // For delegation staking, we just delegate the funds to pool account. - T::Staking::delegate(who, pool_account, reward_account, amount) + fn active_stake(pool: PoolId) -> BalanceOf { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::active_stake(&pool_account).unwrap_or_default() } - fn delegator_bond_extra( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, + fn total_stake(pool: PoolId) -> Staking::Balance { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::total_stake(&pool_account).unwrap_or_default() + } + + fn nominate(pool_id: PoolId, validators: Vec) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::nominate(&pool_account, validators) + } + + fn chill(pool_id: PoolId) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::chill(&pool_account) + } + + fn bond( + who: &T::AccountId, + pool: PoolId, + amount: BalanceOf, + bond_type: BondType, ) -> DispatchResult { - T::Staking::delegate_extra(who, pool_account, amount) + let pool_account = Pallet::::create_bonded_account(pool); + + match bond_type { + BondType::Create => { + // first delegation + let reward_account = Pallet::::create_reward_account(pool); + Staking::delegate(who, &pool_account, &reward_account, amount) + }, + BondType::Later => { + // additional delegation + Staking::delegate_extra(who, &pool_account, amount) + }, + } } - fn delegator_withdraw( - who: &Self::AccountId, - pool_account: &Self::AccountId, - amount: Self::Balance, + fn unbond(pool_id: PoolId, amount: Staking::Balance) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::unbond(&pool_account, amount) + } + + fn withdraw_unbonded(pool_id: PoolId, num_slashing_spans: u32) -> Result { + let pool_account = Pallet::::create_bonded_account(pool_id); + Staking::withdraw_unbonded(pool_account, num_slashing_spans) + } + + fn member_withdraw(who: &T::AccountId, pool: PoolId, amount: BalanceOf) -> DispatchResult { + let pool_account = Pallet::::create_bonded_account(pool); + Staking::withdraw_delegation(&who, &pool_account, amount) + } + + fn has_pending_slash(pool: PoolId) -> bool { + // for transfer stake strategy, slashing is greedy + let pool_account = Pallet::::create_bonded_account(pool); + Staking::has_pending_slash(&pool_account) + } + + fn member_slash( + who: &T::AccountId, + pool: PoolId, + amount: Staking::Balance, + maybe_reporter: Option, ) -> DispatchResult { - T::Staking::withdraw_delegation(who, pool_account, amount) + let pool_account = Pallet::::create_bonded_account(pool); + Staking::delegator_slash(&pool_account, who, amount, maybe_reporter) } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index f76a1fb9ed3a..83aefc7219e7 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -351,7 +351,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use adapter::StakeAdapter; +use adapter::StakeStrategy; use codec::Codec; use frame_support::{ defensive, defensive_assert, ensure, @@ -427,7 +427,7 @@ pub enum ConfigOp { } /// The type of bonding that can happen to a pool. -enum BondType { +pub enum BondType { /// Someone is bonding into the pool upon creation. Create, /// Someone is adding more funds later to this pool. @@ -998,8 +998,7 @@ impl BondedPool { /// /// This is often used for bonding and issuing new funds into the pool. fn balance_to_point(&self, new_funds: BalanceOf) -> BalanceOf { - let bonded_balance = - T::Staking::active_stake(&self.bonded_account()).unwrap_or(Zero::zero()); + let bonded_balance = T::StakeAdapter::active_stake(self.id); Pallet::::balance_to_point(bonded_balance, self.points, new_funds) } @@ -1007,8 +1006,7 @@ impl BondedPool { /// /// This is often used for unbonding. fn points_to_balance(&self, points: BalanceOf) -> BalanceOf { - let bonded_balance = - T::Staking::active_stake(&self.bonded_account()).unwrap_or(Zero::zero()); + let bonded_balance = T::StakeAdapter::active_stake(self.id); Pallet::::point_to_balance(bonded_balance, self.points, points) } @@ -1055,13 +1053,6 @@ impl BondedPool { self } - /// The pools balance that is transferable provided it is expendable by staking pallet. - fn transferable_balance(&self) -> BalanceOf { - let account = self.bonded_account(); - T::StakeAdapter::balance(&account) - .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) - } - fn is_root(&self, who: &T::AccountId) -> bool { self.roles.root.as_ref().map_or(false, |root| root == who) } @@ -1125,8 +1116,7 @@ impl BondedPool { fn ok_to_be_open(&self) -> Result<(), DispatchError> { ensure!(!self.is_destroying(), Error::::CanNotChangeState); - let bonded_balance = - T::Staking::active_stake(&self.bonded_account()).unwrap_or(Zero::zero()); + let bonded_balance = T::StakeAdapter::active_stake(self.id); ensure!(!bonded_balance.is_zero(), Error::::OverflowRisk); let points_to_balance_ratio_floor = self @@ -1255,22 +1245,14 @@ impl BondedPool { amount: BalanceOf, ty: BondType, ) -> Result, DispatchError> { - // Cache the value - let bonded_account = self.bonded_account(); - // We must calculate the points issued *before* we bond who's funds, else points:balance // ratio will be wrong. let points_issued = self.issue(amount); - let reward_account = self.reward_account(); - - match ty { - BondType::Create => - T::StakeAdapter::delegator_bond(who, &bonded_account, &reward_account, amount)?, - // The pool should always be created in such a way its in a state to bond extra, but if - // the active balance is slashed below the minimum bonded or the account cannot be - // found, we exit early. - BondType::Later => T::StakeAdapter::delegator_bond_extra(who, &bonded_account, amount)?, - } + + // The pool should always be created in such a way it is in a state to bond extra, but if + // the active balance is slashed below the minimum bonded or the account cannot be + // found, we exit early. + T::StakeAdapter::bond(who, self.id, amount, ty)?; TotalValueLocked::::mutate(|tvl| { tvl.saturating_accrue(amount); }); @@ -1291,7 +1273,7 @@ impl BondedPool { } fn has_pending_slash(&self) -> bool { - T::Staking::has_pending_slash(&self.bonded_account()) + T::StakeAdapter::has_pending_slash(self.id) } } @@ -1564,8 +1546,8 @@ impl Get for TotalUnbondingPools { fn get() -> u32 { // NOTE: this may be dangerous in the scenario bonding_duration gets decreased because // we would no longer be able to decode `BoundedBTreeMap::, - // TotalUnbondingPools>`, which uses `TotalUnbondingPools` as the bound - T::Staking::bonding_duration() + T::PostUnbondingPoolsWindow::get() + // TotalUnbondingPools>`, werh uses `TotalUnbondingPools` as the bound + T::StakeAdapter::bonding_duration() + T::PostUnbondingPoolsWindow::get() } } @@ -1641,9 +1623,6 @@ pub mod pallet { /// Infallible method for converting `U256` to `Currency::Balance`. type U256ToBalance: Convert>; - /// The interface for nominating. - type Staking: StakingInterface, AccountId = Self::AccountId>; - /// The amount of eras a `SubPools::with_era` pool can exist before it gets merged into the /// `SubPools::no_era` pool. In other words, this is the amount of eras a member will be /// able to withdraw from an unbonding pool which is guaranteed to have the correct ratio of @@ -1655,7 +1634,7 @@ pub mod pallet { type MaxMetadataLen: Get; /// An adapter to support delegated to direct staking. - type StakeAdapter: adapter::StakeAdapter< + type StakeAdapter: adapter::StakeStrategy< AccountId = Self::AccountId, Balance = BalanceOf, >; @@ -1966,6 +1945,8 @@ pub mod pallet { /// The bonded account should only be killed by the staking system when the depositor is /// withdrawing BondedStashKilledPrematurely, + /// Using stake strategy that does not support delegation + DelegationUnsupported, } impl From for Error { @@ -2142,12 +2123,12 @@ pub mod pallet { &mut reward_pool, )?; - let current_era = T::Staking::current_era(); - let unbond_era = T::Staking::bonding_duration().saturating_add(current_era); + let current_era = T::StakeAdapter::current_era(); + let unbond_era = T::StakeAdapter::bonding_duration().saturating_add(current_era); // Unbond in the actual underlying nominator. let unbonding_balance = bonded_pool.dissolve(unbonding_points); - T::Staking::unbond(&bonded_pool.bonded_account(), unbonding_balance)?; + T::StakeAdapter::unbond(bonded_pool.id, unbonding_balance)?; // Note that we lazily create the unbonding pools here if they don't already exist let mut sub_pools = SubPoolsStorage::::get(member.pool_id) @@ -2210,7 +2191,7 @@ pub mod pallet { // For now we only allow a pool to withdraw unbonded if its not destroying. If the pool // is destroying then `withdraw_unbonded` can be used. ensure!(pool.state != PoolState::Destroying, Error::::NotDestroying); - T::Staking::withdraw_unbonded(pool.bonded_account(), num_slashing_spans)?; + T::StakeAdapter::withdraw_unbonded(pool_id, num_slashing_spans)?; Ok(()) } @@ -2247,7 +2228,7 @@ pub mod pallet { let member_account = T::Lookup::lookup(member_account)?; let mut member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; - let current_era = T::Staking::current_era(); + let current_era = T::StakeAdapter::current_era(); let bonded_pool = BondedPool::::get(member.pool_id) .defensive_ok_or::>(DefensiveError::PoolNotFound.into())?; @@ -2268,7 +2249,7 @@ pub mod pallet { // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the // `transferrable_balance` is correct. let stash_killed = - T::Staking::withdraw_unbonded(bonded_pool.bonded_account(), num_slashing_spans)?; + T::StakeAdapter::withdraw_unbonded(bonded_pool.id, num_slashing_spans)?; // defensive-only: the depositor puts enough funds into the stash so that it will only // be destroyed when they are leaving. @@ -2301,14 +2282,10 @@ pub mod pallet { // don't exist. This check is also defensive in cases where the unbond pool does not // update its balance (e.g. a bug in the slashing hook.) We gracefully proceed in // order to ensure members can leave the pool and it can be destroyed. - .min(bonded_pool.transferable_balance()); + .min(T::StakeAdapter::transferable_balance(bonded_pool.id)); - T::StakeAdapter::delegator_withdraw( - &member_account, - &bonded_pool.bonded_account(), - balance_to_unbond, - ) - .defensive()?; + T::StakeAdapter::member_withdraw(&member_account, bonded_pool.id, balance_to_unbond) + .defensive()?; Self::deposit_event(Event::::Withdrawn { member: member_account.clone(), @@ -2423,7 +2400,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); - T::Staking::nominate(&bonded_pool.bonded_account(), validators) + T::StakeAdapter::nominate(bonded_pool.id, validators) } /// Set a new state for the pool. @@ -2596,7 +2573,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); - T::Staking::chill(&bonded_pool.bonded_account()) + T::StakeAdapter::chill(bonded_pool.id) } /// `origin` bonds funds from `extra` for some pool member `member` into their respective @@ -2824,7 +2801,7 @@ pub mod pallet { "Minimum points to balance ratio must be greater than 0" ); assert!( - T::Staking::bonding_duration() < TotalUnbondingPools::::get(), + T::StakeAdapter::bonding_duration() < TotalUnbondingPools::::get(), "There must be more unbonding pools then the bonding duration / so a slash can be applied to relevant unboding pools. (We assume / the bonding duration > slash deffer duration.", @@ -2842,7 +2819,7 @@ impl Pallet { /// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former /// is coming from the staking pallet and the latter two are configured in this pallet. pub fn depositor_min_bond() -> BalanceOf { - T::Staking::minimum_nominator_bond() + T::StakeAdapter::minimum_nominator_bond() .max(MinCreateBond::::get()) .max(MinJoinBond::::get()) .max(T::Currency::minimum_balance()) @@ -2878,7 +2855,7 @@ impl Pallet { "bonded account of dissolving pool should have no consumers" ); defensive_assert!( - T::Staking::total_stake(&bonded_account).unwrap_or_default() == Zero::zero(), + T::StakeAdapter::total_stake(bonded_pool.id) == Zero::zero(), "dissolving pool should not have any stake in the staking pallet" ); @@ -2901,7 +2878,7 @@ impl Pallet { "could not transfer all amount to depositor while dissolving pool" ); defensive_assert!( - T::StakeAdapter::total_balance(&bonded_pool.bonded_account()) == Zero::zero(), + T::StakeAdapter::total_balance(bonded_pool.id) == Zero::zero(), "dissolving pool should not have any balance" ); // NOTE: Defensively force set balance to zero. @@ -3296,10 +3273,9 @@ impl Pallet { // calculate points to be slashed. let member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; - let bonded_account = Self::create_bonded_account(member.pool_id); - ensure!(T::Staking::has_pending_slash(&bonded_account), Error::::NothingToSlash); + ensure!(T::StakeAdapter::has_pending_slash(member.pool_id), Error::::NothingToSlash); - let delegated_balance = T::Staking::delegated_balance(&member_account); + let delegated_balance = T::StakeAdapter::member_delegation_balance(&member_account); let current_balance = member.total_balance(); defensive_assert!( delegated_balance >= current_balance, @@ -3309,9 +3285,9 @@ impl Pallet { // if nothing to slash, return error. ensure!(delegated_balance > current_balance, Error::::NothingToSlash); - T::Staking::delegator_slash( - &bonded_account, + T::StakeAdapter::member_slash( &member_account, + member.pool_id, delegated_balance.defensive_saturating_sub(current_balance), reporter, ) @@ -3480,8 +3456,7 @@ impl Pallet { pool is being destroyed and the depositor is the last member", ); - expected_tvl += - T::Staking::total_stake(&bonded_pool.bonded_account()).unwrap_or_default(); + expected_tvl += T::StakeAdapter::total_stake(bonded_pool.id); Ok(()) })?; @@ -3506,12 +3481,11 @@ impl Pallet { } for (pool_id, _pool) in BondedPools::::iter() { - let pool_account = Pallet::::create_bonded_account(pool_id); let subs = SubPoolsStorage::::get(pool_id).unwrap_or_default(); let sum_unbonding_balance = subs.sum_unbonding_balance(); - let bonded_balance = T::Staking::active_stake(&pool_account).unwrap_or_default(); - let total_balance = T::StakeAdapter::total_balance(&pool_account); + let bonded_balance = T::StakeAdapter::active_stake(pool_id); + let total_balance = T::StakeAdapter::total_balance(pool_id); assert!( total_balance >= bonded_balance + sum_unbonding_balance, @@ -3614,8 +3588,7 @@ impl Pallet { /// If the pool ID does not exist, returns 0 ratio balance to points. Used by runtime API. pub fn api_balance_to_points(pool_id: PoolId, new_funds: BalanceOf) -> BalanceOf { if let Some(pool) = BondedPool::::get(pool_id) { - let bonded_balance = - T::Staking::active_stake(&pool.bonded_account()).unwrap_or(Zero::zero()); + let bonded_balance = T::StakeAdapter::active_stake(pool.id); Pallet::::balance_to_point(bonded_balance, pool.points, new_funds) } else { Zero::zero() diff --git a/substrate/frame/nomination-pools/src/migration.rs b/substrate/frame/nomination-pools/src/migration.rs index 6887fcfa7eca..89fa91e2063d 100644 --- a/substrate/frame/nomination-pools/src/migration.rs +++ b/substrate/frame/nomination-pools/src/migration.rs @@ -1023,10 +1023,7 @@ mod helpers { pub(crate) fn calculate_tvl_by_total_stake() -> BalanceOf { BondedPools::::iter() - .map(|(id, inner)| { - T::Staking::total_stake(&BondedPool { id, inner: inner.clone() }.bonded_account()) - .unwrap_or_default() - }) + .map(|(id, _inner)| T::StakeAdapter::total_stake(id)) .reduce(|acc, total_balance| acc + total_balance) .unwrap_or_default() } diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 7fec33043fbd..23b356a62c31 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -228,61 +228,6 @@ impl sp_staking::StakingInterface for StakingMock { fn unsafe_release_all(_who: &Self::AccountId) { unimplemented!("method currently not used in testing") } - - fn is_delegatee(who: &Self::AccountId) -> bool { - unimplemented!("method currently not used in testing") - } - - /// Effective balance of the delegatee account. - fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { - unimplemented!("method currently not used in testing") - } - - /// Delegate funds to `Delegatee`. - fn delegate( - who: &Self::AccountId, - delegatee: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - unimplemented!("method currently not used in testing") - } - - /// Add more delegation to the pool account. - fn delegate_extra( - who: &Self::AccountId, - delegatee: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - unimplemented!("method currently not used in testing") - } - - /// Withdraw delegation from pool account to self. - fn withdraw_delegation( - who: &Self::AccountId, - delegatee: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - unimplemented!("method currently not used in testing") - } - - /// Does the delegatee have any pending slash. - fn has_pending_slash(delegatee: &Self::AccountId) -> bool { - false - } - - fn delegator_slash( - delegatee: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, - ) -> sp_runtime::DispatchResult { - unimplemented!("method currently not used in testing") - } - - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { - unimplemented!("method currently not used in testing") - } } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] @@ -360,13 +305,12 @@ impl pools::Config for Runtime { type RewardCounter = RewardCounter; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type Staking = StakingMock; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type PalletId = PoolsPalletId; type MaxMetadataLen = MaxMetadataLen; type MaxUnbonding = MaxUnbonding; type MaxPointsToBalance = frame_support::traits::ConstU8<10>; - type StakeAdapter = adapter::TransferStake; + type StakeAdapter = adapter::TransferStake; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 54dfe198c660..189294ffe9a2 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -181,13 +181,12 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type Staking = Staking; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = ConstU32<8>; type MaxPointsToBalance = ConstU8<10>; type PalletId = PoolsPalletId; - type StakeAdapter = pallet_nomination_pools::adapter::TransferStake; + type StakeAdapter = pallet_nomination_pools::adapter::TransferStake; } type Block = frame_system::mocking::MockBlock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index bd3af7d103de..5302fdbebb2d 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -39,12 +39,12 @@ use sp_runtime::{ Perbill, Percent, }; use sp_staking::{ - currency_to_vote::CurrencyToVote, - delegation::DelegateeSupport, - offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, - StakingAccount::{self, Controller, Stash}, - StakingInterface, + currency_to_vote::CurrencyToVote, + delegation::DelegateeSupport, + offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, + EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, + StakingAccount::{self, Controller, Stash}, + StakingInterface, }; use sp_std::prelude::*; @@ -1870,64 +1870,6 @@ impl StakingInterface for Pallet { fn unsafe_release_all(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } - - fn is_delegatee(_who: &Self::AccountId) -> bool { - defensive!("is_delegatee is not supported"); - false - } - - fn delegatee_balance(_who: &Self::AccountId) -> Self::Balance { - defensive!("delegatee_balance is not supported"); - BalanceOf::::zero() - } - - /// Delegate funds to `Delegatee`. - fn delegate( - _who: &Self::AccountId, - _delegatee: &Self::AccountId, - _reward_account: &Self::AccountId, - _amount: Self::Balance, - ) -> DispatchResult { - Err(DispatchError::Other("delegate is not supported")) - } - - /// Add more delegation to the pool account. - fn delegate_extra( - _who: &Self::AccountId, - _delegatee: &Self::AccountId, - _amount: Self::Balance, - ) -> DispatchResult { - Err(DispatchError::Other("delegate_extra is not supported")) - } - - /// Withdraw delegation from pool account to self. - fn withdraw_delegation( - _who: &Self::AccountId, - _delegatee: &Self::AccountId, - _amount: Self::Balance, - ) -> DispatchResult { - Err(DispatchError::Other("withdraw_delegation is not supported")) - } - - /// Does the delegatee have any pending slash. - fn has_pending_slash(_delegatee: &Self::AccountId) -> bool { - // slashing is greedy, so no pending slash. - false - } - - fn delegator_slash( - _delegatee: &Self::AccountId, - _delegator: &Self::AccountId, - _value: Self::Balance, - _maybe_reporter: Option, - ) -> sp_runtime::DispatchResult { - Err(DispatchError::Other("delegator_slash is not supported")) - } - - fn delegated_balance(_delegator: &Self::AccountId) -> Self::Balance { - defensive!("delegated_balance is not supported"); - BalanceOf::::zero() - } } /// Standard implementation of `DelegateeSupport` that supports only direct staking and no diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 861eda301646..6fce9dd84c26 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,9 +37,9 @@ use sp_runtime::{ }; use sp_staking::{ - delegation::DelegateeSupport, - EraIndex, Page, SessionIndex, - StakingAccount::{self, Controller, Stash}, + delegation::DelegateeSupport, + EraIndex, Page, SessionIndex, + StakingAccount::{self, Controller, Stash}, }; use sp_std::prelude::*; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index c8a9f366d701..7490f97cecdb 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -15,9 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::StakingInterface; use codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime::Saturating; +use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; /// A trait that can be used as a plugin to support delegation based accounts, called `Delegatee`. @@ -62,3 +63,49 @@ pub trait DelegateeSupport { /// delegators. This function should bookkeep the slash to be applied later. fn report_slash(who: &Self::AccountId, slash: Self::Balance); } + +pub trait DelegatedStakeInterface: StakingInterface { + /// Returns true if who is a `delegatee` account. + fn is_delegatee(who: &Self::AccountId) -> bool; + + /// Effective balance of the delegatee account. + fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; + + /// Delegate funds to `Delegatee`. + fn delegate( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + reward_account: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Add more delegation to the `delegatee`. + fn delegate_extra( + delegator: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Withdraw or revoke delegation to `delegatee`. + fn withdraw_delegation( + who: &Self::AccountId, + delegatee: &Self::AccountId, + amount: Self::Balance, + ) -> DispatchResult; + + /// Returns true if there are pending slashes posted to the `delegatee` account. + fn has_pending_slash(delegatee: &Self::AccountId) -> bool; + + /// Apply a pending slash to a `delegatee` by slashing `value` from `delegator`. + /// + /// If a reporter is provided, the reporter will receive a fraction of the slash as reward. + fn delegator_slash( + delegatee: &Self::AccountId, + delegator: &Self::AccountId, + value: Self::Balance, + maybe_reporter: Option, + ) -> sp_runtime::DispatchResult; + + /// Returns the total amount of funds delegated by a `delegator`. + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; +} diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 5131d0d9dc36..d1ae0f65bf1f 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -309,53 +309,6 @@ pub trait StakingInterface { #[cfg(feature = "runtime-benchmarks")] fn set_current_era(era: EraIndex); - - // FIXME(ank4n): Break down this trait maybe? It is too bloated currently. - - /// Returns true if who is a `delegatee` account. - fn is_delegatee(who: &Self::AccountId) -> bool; - - /// Effective balance of the delegatee account. - fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; - - /// Delegate funds to `Delegatee`. - fn delegate( - delegator: &Self::AccountId, - delegatee: &Self::AccountId, - reward_account: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Add more delegation to the `delegatee`. - fn delegate_extra( - delegator: &Self::AccountId, - delegatee: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Withdraw or revoke delegation to `delegatee`. - fn withdraw_delegation( - who: &Self::AccountId, - delegatee: &Self::AccountId, - amount: Self::Balance, - ) -> DispatchResult; - - /// Returns true if there are pending slashes posted to the `delegatee` account. - fn has_pending_slash(delegatee: &Self::AccountId) -> bool; - - /// Apply a pending slash to a `delegatee` by slashing `value` from `delegator`. - /// - /// If a reporter is provided, the reporter will receive a fraction of the slash as reward. - fn delegator_slash( - delegatee: &Self::AccountId, - delegator: &Self::AccountId, - value: Self::Balance, - maybe_reporter: Option, - ) -> sp_runtime::DispatchResult; - - - /// Returns the total amount of funds delegated by a `delegator`. - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; } /// The amount of exposure for an era that an individual nominator has (susceptible to slashing). From df13e493880ac9f7b63e6f843b741775e50e71c2 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 28 Feb 2024 11:22:38 +0100 Subject: [PATCH 182/202] small doc update --- substrate/primitives/staking/src/delegation.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 7490f97cecdb..6144dcc53bc6 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -64,14 +64,16 @@ pub trait DelegateeSupport { fn report_slash(who: &Self::AccountId, slash: Self::Balance); } +/// Trait that extends on [`StakingInterface`] to provide additional capability to delegate funds to +/// an account. pub trait DelegatedStakeInterface: StakingInterface { /// Returns true if who is a `delegatee` account. fn is_delegatee(who: &Self::AccountId) -> bool; - /// Effective balance of the delegatee account. + /// Effective balance of the `delegatee` account. fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; - /// Delegate funds to `Delegatee`. + /// Delegate funds to `delegatee`. fn delegate( delegator: &Self::AccountId, delegatee: &Self::AccountId, From 11bb08403e60438864e5af68cfb8d95bd2ee7172 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 28 Feb 2024 11:35:02 +0100 Subject: [PATCH 183/202] reorder methods --- substrate/frame/delegated-staking/src/impls.rs | 12 ++++-------- substrate/primitives/staking/src/delegation.rs | 9 +++------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 67ff007b6d27..34d409eb1622 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -197,10 +197,6 @@ impl StakingInterface for Pallet { } impl DelegatedStakeInterface for Pallet { - fn is_delegatee(who: &Self::AccountId) -> bool { - Self::is_delegatee(who) - } - /// Effective balance of the delegatee account. fn delegatee_balance(who: &Self::AccountId) -> Self::Balance { Delegatee::::from(who) @@ -208,6 +204,10 @@ impl DelegatedStakeInterface for Pallet { .unwrap_or_default() } + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { + Delegation::::get(delegator).map(|d| d.amount).unwrap_or_default() + } + /// Delegate funds to `Delegatee`. fn delegate( who: &Self::AccountId, @@ -266,10 +266,6 @@ impl DelegatedStakeInterface for Pallet { ) -> sp_runtime::DispatchResult { Pallet::::do_slash(delegatee.clone(), delegator.clone(), value, maybe_reporter) } - - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { - Delegation::::get(delegator).map(|d| d.amount).unwrap_or_default() - } } impl DelegateeSupport for Pallet { diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 6144dcc53bc6..5bde51f5a0ea 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -67,12 +67,12 @@ pub trait DelegateeSupport { /// Trait that extends on [`StakingInterface`] to provide additional capability to delegate funds to /// an account. pub trait DelegatedStakeInterface: StakingInterface { - /// Returns true if who is a `delegatee` account. - fn is_delegatee(who: &Self::AccountId) -> bool; - /// Effective balance of the `delegatee` account. fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; + /// Returns the total amount of funds delegated by a `delegator`. + fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; + /// Delegate funds to `delegatee`. fn delegate( delegator: &Self::AccountId, @@ -107,7 +107,4 @@ pub trait DelegatedStakeInterface: StakingInterface { value: Self::Balance, maybe_reporter: Option, ) -> sp_runtime::DispatchResult; - - /// Returns the total amount of funds delegated by a `delegator`. - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; } From 78ea6aee79511c1875e49d45affb418bf0e90c02 Mon Sep 17 00:00:00 2001 From: Ankan Date: Wed, 28 Feb 2024 20:27:51 +0100 Subject: [PATCH 184/202] minor --- substrate/primitives/staking/src/delegation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 5bde51f5a0ea..84a50273e291 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -68,7 +68,7 @@ pub trait DelegateeSupport { /// an account. pub trait DelegatedStakeInterface: StakingInterface { /// Effective balance of the `delegatee` account. - fn delegatee_balance(who: &Self::AccountId) -> Self::Balance; + fn delegatee_balance(delegatee: &Self::AccountId) -> Self::Balance; /// Returns the total amount of funds delegated by a `delegator`. fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; @@ -90,7 +90,7 @@ pub trait DelegatedStakeInterface: StakingInterface { /// Withdraw or revoke delegation to `delegatee`. fn withdraw_delegation( - who: &Self::AccountId, + delegator: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult; From ef3b3e79be94884cc6c39a70c00635888f678b49 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:19:49 +0100 Subject: [PATCH 185/202] Update substrate/frame/delegated-staking/Cargo.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gonçalo Pestana --- substrate/frame/delegated-staking/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/delegated-staking/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml index 4b278af64cf3..b4b9768256c6 100644 --- a/substrate/frame/delegated-staking/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "FRAME safe-mode pallet" +description = "FRAME delegated staking pallet" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] From b4ef3972646740c1db6fdcf53e8cc8cfeffbf270 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 29 Feb 2024 13:46:38 +0100 Subject: [PATCH 186/202] fix pr feedbacks --- .../frame/delegated-staking/src/impls.rs | 45 ++++++++++--------- substrate/frame/delegated-staking/src/lib.rs | 2 +- .../primitives/staking/src/delegation.rs | 4 ++ 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 34d409eb1622..87be6e73eb5a 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -35,12 +35,10 @@ impl StakingInterface for Pallet { } fn minimum_validator_bond() -> Self::Balance { - defensive_assert!(false, "not supported for delegated impl of staking interface"); T::CoreStaking::minimum_validator_bond() } fn stash_by_ctrl(_controller: &Self::AccountId) -> Result { - defensive_assert!(false, "not supported for delegated impl of staking interface"); // ctrl are deprecated, just return err. Err(Error::::NotSupported.into()) } @@ -90,7 +88,7 @@ impl StakingInterface for Pallet { payee: &Self::AccountId, ) -> DispatchResult { // ensure who is not already staked - ensure!(T::CoreStaking::status(who).is_err(), Error::::NotDelegatee); + ensure!(T::CoreStaking::status(who).is_err(), Error::::AlreadyStaker); let delegatee = Delegatee::::from(who)?; ensure!(delegatee.available_to_bond() >= value, Error::::NotEnoughFunds); @@ -100,18 +98,18 @@ impl StakingInterface for Pallet { } fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotDelegatee); return T::CoreStaking::nominate(who, validators); } fn chill(who: &Self::AccountId) -> DispatchResult { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotDelegatee); return T::CoreStaking::chill(who); } fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { - let delegation_register = >::get(who).ok_or(Error::::NotDelegatee)?; - ensure!(delegation_register.stakeable_balance() >= extra, Error::::NotEnoughFunds); + let ledger = >::get(who).ok_or(Error::::NotDelegatee)?; + ensure!(ledger.stakeable_balance() >= extra, Error::::NotEnoughFunds); T::CoreStaking::bond_extra(who, extra) } @@ -127,40 +125,35 @@ impl StakingInterface for Pallet { /// /// Funds are moved to unclaimed_withdrawals register of the `DelegateeLedger`. fn withdraw_unbonded( - pool_acc: Self::AccountId, + delegatee_acc: Self::AccountId, num_slashing_spans: u32, ) -> Result { - Pallet::::withdraw_unbonded(&pool_acc, num_slashing_spans) + Pallet::::withdraw_unbonded(&delegatee_acc, num_slashing_spans) .map(|delegatee| delegatee.ledger.total_delegated.is_zero()) } fn desired_validator_count() -> u32 { - defensive_assert!(false, "not supported for delegated impl of staking interface"); T::CoreStaking::desired_validator_count() } fn election_ongoing() -> bool { - defensive_assert!(false, "not supported for delegated impl of staking interface"); T::CoreStaking::election_ongoing() } fn force_unstake(_who: Self::AccountId) -> DispatchResult { - defensive_assert!(false, "not supported for delegated impl of staking interface"); Err(Error::::NotSupported.into()) } fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool { - defensive_assert!(false, "not supported for delegated impl of staking interface"); T::CoreStaking::is_exposed_in_era(who, era) } fn status(who: &Self::AccountId) -> Result, DispatchError> { - ensure!(Self::is_delegatee(who), Error::::NotSupported); + ensure!(Self::is_delegatee(who), Error::::NotDelegatee); T::CoreStaking::status(who) } fn is_validator(who: &Self::AccountId) -> bool { - defensive_assert!(false, "not supported for delegated impl of staking interface"); T::CoreStaking::is_validator(who) } @@ -173,7 +166,7 @@ impl StakingInterface for Pallet { } fn unsafe_release_all(_who: &Self::AccountId) { - defensive_assert!(false, "not supported for delegated impl of staking interface"); + defensive_assert!(false, "unsafe_release_all is not supported"); } #[cfg(feature = "runtime-benchmarks")] @@ -228,7 +221,7 @@ impl DelegatedStakeInterface for Pallet { ) } - /// Add more delegation to the pool account. + /// Add more delegation to the delegatee account. fn delegate_extra( who: &Self::AccountId, delegatee: &Self::AccountId, @@ -241,17 +234,25 @@ impl DelegatedStakeInterface for Pallet { ) } - /// Withdraw delegation from pool account to self. + /// Withdraw delegation of `delegator` to `delegatee`. + /// + /// If there are funds in `delegatee` account that can be withdrawn, then those funds would be + /// unlocked/released in the delegator's account. fn withdraw_delegation( - who: &Self::AccountId, + delegator: &Self::AccountId, delegatee: &Self::AccountId, amount: Self::Balance, ) -> DispatchResult { - // fixme(ank4n): This should not require slashing spans. - Pallet::::release(RawOrigin::Signed(delegatee.clone()).into(), who.clone(), amount, 0) + // fixme(ank4n): Can this not require slashing spans? + Pallet::::release( + RawOrigin::Signed(delegatee.clone()).into(), + delegator.clone(), + amount, + 0, + ) } - /// Does the delegatee have any pending slash. + /// Returns true if the `delegatee` have any slash pending to be applied. fn has_pending_slash(delegatee: &Self::AccountId) -> bool { Delegatee::::from(delegatee) .map(|d| !d.ledger.pending_slash.is_zero()) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 5b180f3e0b21..0bfb57053d35 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -213,7 +213,7 @@ pub mod pallet { pub enum Error { /// The account cannot perform this operation. NotAllowed, - /// An existing staker cannot become a `delegatee`. + /// An existing staker cannot perform this action. AlreadyStaker, /// Reward Destination cannot be `delegatee` account. InvalidRewardDestination, diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 84a50273e291..c6e5a1029fc9 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -46,6 +46,10 @@ pub trait DelegateeSupport { /// Returns true if `delegatee` is restricted to update which account they can receive their /// staking rewards. + /// + /// For `delegatee` accounts we restrict the reward destination to be the same as the + /// `delegatee` account itself. This is since the actual `delegatee` balances is not considered + /// while staking. Instead, their balance is made up of multiple child delegators. fn restrict_reward_destination( _who: &Self::AccountId, _reward_destination: Option, From 2877d0e9a05f9c0c48ceabb5425832db2c5b87c3 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 29 Feb 2024 13:51:39 +0100 Subject: [PATCH 187/202] rename delegator balance --- substrate/frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 4 ++-- substrate/frame/nomination-pools/src/adapter.rs | 2 +- substrate/frame/nomination-pools/src/lib.rs | 2 +- substrate/primitives/staking/src/delegation.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 87be6e73eb5a..ef0edcf61563 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -197,7 +197,7 @@ impl DelegatedStakeInterface for Pallet { .unwrap_or_default() } - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance { + fn delegator_balance(delegator: &Self::AccountId) -> Self::Balance { Delegation::::get(delegator).map(|d| d.amount).unwrap_or_default() } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 0bfb57053d35..97acc10d5576 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -25,7 +25,7 @@ //! //! ## Goals //! -//! The direct nominators on Staking pallet does not scale well. NominationPool was created to +//! Direct nomination on the Staking pallet does not scale well. Nominations pools were created to //! address this by pooling delegator funds into one account and then staking it. This though had //! a very important limitation that the funds were moved from delegator account to pool account //! and hence the delegator lost control over their funds for using it for other purposes such as @@ -44,7 +44,7 @@ //! similar to `NominationPool`. //! //! ## Key Terminologies -//! - *delegatee*: An account who accepts delegations from other accounts (called `Delegators`). +//! - *Delegatee*: An account who accepts delegations from other accounts. //! - *Delegator*: An account who delegates their funds to a `delegatee`. //! - *DelegateeLedger*: A data structure that stores important information about the `delegatee` //! such as their total delegated stake. diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 6546ef5f38d7..c7bf6868e953 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -224,7 +224,7 @@ impl< } fn member_delegation_balance(member_account: &T::AccountId) -> Staking::Balance { - Staking::delegated_balance(member_account) + Staking::delegator_balance(member_account) } fn active_stake(pool: PoolId) -> BalanceOf { diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 83aefc7219e7..bf75d85097e5 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1633,7 +1633,7 @@ pub mod pallet { /// The maximum length, in bytes, that a pools metadata maybe. type MaxMetadataLen: Get; - /// An adapter to support delegated to direct staking. + /// Staking adapter to support different staking strategies. type StakeAdapter: adapter::StakeStrategy< AccountId = Self::AccountId, Balance = BalanceOf, diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index c6e5a1029fc9..8cf986dad1ff 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -75,7 +75,7 @@ pub trait DelegatedStakeInterface: StakingInterface { fn delegatee_balance(delegatee: &Self::AccountId) -> Self::Balance; /// Returns the total amount of funds delegated by a `delegator`. - fn delegated_balance(delegator: &Self::AccountId) -> Self::Balance; + fn delegator_balance(delegator: &Self::AccountId) -> Self::Balance; /// Delegate funds to `delegatee`. fn delegate( From 3de4c4aa710d47eb9b1d6beb828eae3deebd7d30 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 29 Feb 2024 14:38:57 +0100 Subject: [PATCH 188/202] add to docs --- substrate/frame/delegated-staking/src/lib.rs | 21 ++++++++++++------- .../primitives/staking/src/delegation.rs | 12 +++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 97acc10d5576..1d0c25e98a52 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -20,8 +20,15 @@ //! An abstraction over staking pallet to support delegation of funds to a `delegatee` account which //! can use all the delegated funds to it in the staking pallet as if its own fund. //! -//! NOTE: The pallet exposes extrinsics which are not yet meant to be exposed in the runtime but -//! only to be used by other pallets in the same runtime. +//! NOTE: The pallet exposes some dispatchable calls already, but they might not be fully usable +//! from outside the runtime. In the current version, the pallet is meant to be used by other +//! pallets in the same runtime. Eventually though, expect those calls to be functionally complete +//! and usable by off-chain programs as well as xcm based multi locations. +//! +//! Declaring dispatchable still has the benefit of being transactable for unit tests as well as +//! aligned with general direction of moving towards a permissionless pallet. For example, we could +//! clearly signal who is the expected signer of any interaction with this pallet and take into +//! security considerations already. //! //! ## Goals //! @@ -44,12 +51,12 @@ //! similar to `NominationPool`. //! //! ## Key Terminologies -//! - *Delegatee*: An account who accepts delegations from other accounts. -//! - *Delegator*: An account who delegates their funds to a `delegatee`. -//! - *DelegateeLedger*: A data structure that stores important information about the `delegatee` +//! - **Delegatee**: An account who accepts delegations from other accounts. +//! - **Delegator**: An account who delegates their funds to a `delegatee`. +//! - **DelegateeLedger**: A data structure that stores important information about the `delegatee` //! such as their total delegated stake. -//! - *Delegation*: A data structure that stores the amount of funds delegated to a `delegatee` by a -//! `delegator`. +//! - **Delegation**: A data structure that stores the amount of funds delegated to a `delegatee` by +//! a `delegator`. //! //! ## Interface //! diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 8cf986dad1ff..7d970d567d9e 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -72,12 +72,16 @@ pub trait DelegateeSupport { /// an account. pub trait DelegatedStakeInterface: StakingInterface { /// Effective balance of the `delegatee` account. + /// + /// This takes into account any pending slashes to `Delegatee`. fn delegatee_balance(delegatee: &Self::AccountId) -> Self::Balance; /// Returns the total amount of funds delegated by a `delegator`. fn delegator_balance(delegator: &Self::AccountId) -> Self::Balance; /// Delegate funds to `delegatee`. + /// + /// Only used for the initial delegation. Use [`Self::delegate_extra`] to add more delegation. fn delegate( delegator: &Self::AccountId, delegatee: &Self::AccountId, @@ -86,6 +90,8 @@ pub trait DelegatedStakeInterface: StakingInterface { ) -> DispatchResult; /// Add more delegation to the `delegatee`. + /// + /// If this is the first delegation, use [`Self::delegate`] instead. fn delegate_extra( delegator: &Self::AccountId, delegatee: &Self::AccountId, @@ -93,6 +99,9 @@ pub trait DelegatedStakeInterface: StakingInterface { ) -> DispatchResult; /// Withdraw or revoke delegation to `delegatee`. + /// + /// If there are `delegatee` funds upto `amount` available to withdraw, then those funds would be + /// released to the `delegator` fn withdraw_delegation( delegator: &Self::AccountId, delegatee: &Self::AccountId, @@ -100,6 +109,9 @@ pub trait DelegatedStakeInterface: StakingInterface { ) -> DispatchResult; /// Returns true if there are pending slashes posted to the `delegatee` account. + /// + /// Slashes to `delegatee` account are not immediate and are applied lazily. Since `delegatee` + /// has an unbounded number of delegators, immediate slashing is not possible. fn has_pending_slash(delegatee: &Self::AccountId) -> bool; /// Apply a pending slash to a `delegatee` by slashing `value` from `delegator`. From 57e8992339ca8f270a1a295c092471b1c8bf826b Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 1 Mar 2024 03:38:13 +0100 Subject: [PATCH 189/202] rename error --- substrate/frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 18 +++++++++--------- substrate/frame/delegated-staking/src/tests.rs | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index ef0edcf61563..caf6b9aa4281 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -88,7 +88,7 @@ impl StakingInterface for Pallet { payee: &Self::AccountId, ) -> DispatchResult { // ensure who is not already staked - ensure!(T::CoreStaking::status(who).is_err(), Error::::AlreadyStaker); + ensure!(T::CoreStaking::status(who).is_err(), Error::::AlreadyStaking); let delegatee = Delegatee::::from(who)?; ensure!(delegatee.available_to_bond() >= value, Error::::NotEnoughFunds); diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1d0c25e98a52..5da6ab1eb937 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -28,13 +28,13 @@ //! Declaring dispatchable still has the benefit of being transactable for unit tests as well as //! aligned with general direction of moving towards a permissionless pallet. For example, we could //! clearly signal who is the expected signer of any interaction with this pallet and take into -//! security considerations already. +//! account any security considerations associated with those interactions. //! //! ## Goals //! //! Direct nomination on the Staking pallet does not scale well. Nominations pools were created to //! address this by pooling delegator funds into one account and then staking it. This though had -//! a very important limitation that the funds were moved from delegator account to pool account +//! a very critical limitation that the funds were moved from delegator account to pool account //! and hence the delegator lost control over their funds for using it for other purposes such as //! governance. This pallet aims to solve this by extending the staking pallet to support a new //! primitive function: delegation of funds to an account for the intent of staking. @@ -48,7 +48,7 @@ //! delegators. Or part of the reward can go to a insurance fund that can be used to cover any //! potential future slashes. The goal is to eventually allow foreign MultiLocations //! (smart contracts or pallets on another chain) to build their own pooled staking solutions -//! similar to `NominationPool`. +//! similar to `NominationPools`. //! //! ## Key Terminologies //! - **Delegatee**: An account who accepts delegations from other accounts. @@ -96,7 +96,7 @@ //! pending slash never exceeds staked amount and would freeze further withdraws until pending //! slashes are applied. //! -//! `NominationPool` can apply slash for all its members by calling +//! The user of this pallet can apply slash using //! [StakingInterface::delegator_slash](sp_staking::StakingInterface::delegator_slash). //! //! ## Migration from Nominator to Delegatee @@ -221,7 +221,7 @@ pub mod pallet { /// The account cannot perform this operation. NotAllowed, /// An existing staker cannot perform this action. - AlreadyStaker, + AlreadyStaking, /// Reward Destination cannot be `delegatee` account. InvalidRewardDestination, /// Delegation conditions are not met. @@ -232,7 +232,7 @@ pub mod pallet { InvalidDelegation, /// The account does not have enough funds to perform the operation. NotEnoughFunds, - /// Not an existing `delegatee` account. + /// Not an existing delegatee account. NotDelegatee, /// Not a Delegator account. NotDelegator, @@ -301,7 +301,7 @@ pub mod pallet { ensure!(!Self::is_delegator(&who), Error::::NotAllowed); // They cannot be already a direct staker in the staking pallet. - ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); + ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaking); // Reward account cannot be same as `delegatee` account. ensure!(reward_account != who, Error::::InvalidRewardDestination); @@ -380,7 +380,7 @@ pub mod pallet { // Ensure delegator is sane. ensure!(!Self::is_delegatee(&delegator), Error::::NotAllowed); ensure!(!Self::is_delegator(&delegator), Error::::NotAllowed); - ensure!(Self::not_direct_staker(&delegator), Error::::AlreadyStaker); + ensure!(Self::not_direct_staker(&delegator), Error::::AlreadyStaking); // ensure delegatee is sane. ensure!(Self::is_delegatee(&delegatee), Error::::NotDelegatee); @@ -410,7 +410,7 @@ pub mod pallet { // ensure delegator is sane. ensure!(Delegation::::can_delegate(&who, &delegatee), Error::::InvalidDelegation); - ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaker); + ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaking); // ensure delegatee is sane. ensure!( diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 9d29727d70d2..8b01536f558a 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -68,7 +68,7 @@ fn cannot_become_delegatee() { RawOrigin::Signed(mock::GENESIS_VALIDATOR).into(), 100 ), - Error::::AlreadyStaker + Error::::AlreadyStaking ); // an existing nominator cannot become delegatee @@ -77,14 +77,14 @@ fn cannot_become_delegatee() { RawOrigin::Signed(mock::GENESIS_NOMINATOR_ONE).into(), 100 ), - Error::::AlreadyStaker + Error::::AlreadyStaking ); assert_noop!( DelegatedStaking::register_as_delegatee( RawOrigin::Signed(mock::GENESIS_NOMINATOR_TWO).into(), 100 ), - Error::::AlreadyStaker + Error::::AlreadyStaking ); }); } @@ -448,14 +448,14 @@ mod staking_integration { RawOrigin::Signed(GENESIS_NOMINATOR_ONE).into(), 201 ), - Error::::AlreadyStaker + Error::::AlreadyStaking ); assert_noop!( DelegatedStaking::register_as_delegatee( RawOrigin::Signed(GENESIS_VALIDATOR).into(), 201 ), - Error::::AlreadyStaker + Error::::AlreadyStaking ); }); } From e95d800770504b146fda08e3911cb82c0a72bc34 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 1 Mar 2024 04:05:14 +0100 Subject: [PATCH 190/202] refactor types --- substrate/frame/delegated-staking/src/lib.rs | 28 +----- .../frame/delegated-staking/src/tests.rs | 26 ------ .../frame/delegated-staking/src/types.rs | 90 ++++++++++--------- 3 files changed, 53 insertions(+), 91 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 5da6ab1eb937..06086390bf85 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -75,6 +75,8 @@ //! the funds are migrated, the `delegator` can use the funds for other purposes which allows //! usage of held funds in an account, such as governance. //! - `delegate_funds`: Delegate funds to a `delegatee` account and update the bond to staking. +//! - `apply_slash`: If there is a pending slash in `delegatee` ledger, the passed delegator's +//! balance is slashed by the amount and the slash is removed from the delegatee ledger. //! //! #### [Staking Interface](StakingInterface) //! This pallet reimplements the staking interface as a wrapper implementation over @@ -244,8 +246,6 @@ pub mod pallet { WithdrawFailed, /// Operation not supported by this pallet. NotSupported, - /// Account does not accept delegations. - NotAcceptingDelegations, } /// A reason for placing a hold on funds. @@ -413,10 +413,7 @@ pub mod pallet { ensure!(Self::not_direct_staker(&who), Error::::AlreadyStaking); // ensure delegatee is sane. - ensure!( - DelegateeLedger::::can_accept_delegation(&delegatee), - Error::::NotAcceptingDelegations - ); + ensure!(Self::is_delegatee(&delegatee), Error::::NotDelegatee); let delegator_balance = T::Currency::reducible_balance(&who, Preservation::Preserve, Fortitude::Polite); @@ -428,28 +425,12 @@ pub mod pallet { Self::do_bond(&delegatee, amount) } - /// Toggle delegatee status to start or stop accepting new delegations. - /// - /// This can only be used by existing delegates. If not a delegatee yet, use - /// [Call::register_as_delegatee] first. - #[pallet::call_index(5)] - #[pallet::weight(Weight::default())] - pub fn toggle_delegatee_status(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - - let delegatee = Delegatee::::from(&who)?; - let should_block = !delegatee.ledger.blocked; - delegatee.update_status(should_block).save(); - - Ok(()) - } - /// Apply slash to a delegator account. /// /// `Delegatee` accounts with pending slash in their ledger can call this to apply slash to /// one of its `delegator` account. Each slash to a delegator account needs to be posted /// separately until all pending slash is cleared. - #[pallet::call_index(6)] + #[pallet::call_index(5)] #[pallet::weight(Weight::default())] pub fn apply_slash( origin: OriginFor, @@ -571,7 +552,6 @@ impl Pallet { amount: BalanceOf, ) -> DispatchResult { let mut ledger = DelegateeLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; - debug_assert!(!ledger.blocked); let new_delegation_amount = if let Some(existing_delegation) = Delegation::::get(delegator) { diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 8b01536f558a..a278fd8850ad 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -460,32 +460,6 @@ mod staking_integration { }); } - #[test] - fn toggle_delegatee_status() { - ExtBuilder::default().build_and_execute(|| { - assert_ok!(DelegatedStaking::register_as_delegatee(RawOrigin::Signed(200).into(), 201)); - - // delegation works - fund(&300, 1000); - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); - - // delegate blocks delegation - assert_ok!(DelegatedStaking::toggle_delegatee_status(RawOrigin::Signed(200).into())); - - // cannot delegate to it anymore - assert_noop!( - DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100), - Error::::NotAcceptingDelegations - ); - - // delegate can unblock delegation - assert_ok!(DelegatedStaking::toggle_delegatee_status(RawOrigin::Signed(200).into())); - - // delegation works again - assert_ok!(DelegatedStaking::delegate_funds(RawOrigin::Signed(300).into(), 200, 100)); - }); - } - #[test] fn slash_works() { ExtBuilder::default().build_and_execute(|| { diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index ec16c3da3987..0dcdcc0659a4 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -41,14 +41,20 @@ pub struct Delegation { } impl Delegation { + /// Get delegation of a `delegator`. pub(crate) fn get(delegator: &T::AccountId) -> Option { >::get(delegator) } + /// Create and return a new delegation instance. pub(crate) fn from(delegatee: &T::AccountId, amount: BalanceOf) -> Self { Delegation { delegatee: delegatee.clone(), amount } } + /// Ensure the delegator is either a new delegator or they are adding more delegation to the + /// existing delegatee. + /// + /// Delegators are prevented from delegating to multiple delegatees at the same time. pub(crate) fn can_delegate(delegator: &T::AccountId, delegatee: &T::AccountId) -> bool { Delegation::::get(delegator) .map(|delegation| delegation.delegatee == delegatee.clone()) @@ -71,6 +77,7 @@ impl Delegation { Some(Delegation::from(&self.delegatee, updated_delegation)) } + /// Save self to storage. If the delegation amount is zero, remove the delegation. pub(crate) fn save_or_kill(self, key: &T::AccountId) { // Clean up if no delegation left. if self.amount == Zero::zero() { @@ -87,7 +94,6 @@ impl Delegation { /// This keeps track of the active balance of the `delegatee` that is made up from the funds that /// are currently delegated to this `delegatee`. It also tracks the pending slashes yet to be /// applied among other things. -// FIXME(ank4n): Break up into two storage items - bookkeeping stuff and settings stuff. #[derive(Default, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] pub struct DelegateeLedger { @@ -98,42 +104,41 @@ pub struct DelegateeLedger { pub total_delegated: BalanceOf, /// Funds that are withdrawn from core staking but not released to delegator/s. It is a subset /// of `total_delegated` and can never be greater than it. + /// + /// We need this register to ensure that the `delegatee` does not bond funds from delegated + /// funds that are withdrawn and should be claimed by delegators. // FIXME(ank4n): Check/test about rebond: where delegator rebond what is unlocking. #[codec(compact)] pub unclaimed_withdrawals: BalanceOf, - /// Slashes that are not yet applied. + /// Slashes that are not yet applied. This affects the effective balance of the `delegatee`. #[codec(compact)] pub pending_slash: BalanceOf, - /// Whether this `delegatee` is blocked from receiving new delegations. - pub blocked: bool, } impl DelegateeLedger { + /// Create a new instance of `DelegateeLedger`. pub(crate) fn new(reward_destination: &T::AccountId) -> Self { DelegateeLedger { payee: reward_destination.clone(), total_delegated: Zero::zero(), unclaimed_withdrawals: Zero::zero(), pending_slash: Zero::zero(), - blocked: false, } } + /// Get `DelegateeLedger` from storage. pub(crate) fn get(key: &T::AccountId) -> Option { >::get(key) } - pub(crate) fn can_accept_delegation(delegatee: &T::AccountId) -> bool { - DelegateeLedger::::get(delegatee) - .map(|ledger| !ledger.blocked) - .unwrap_or(false) - } - + /// Save self to storage with the given key. pub(crate) fn save(self, key: &T::AccountId) { >::insert(key, self) } /// Effective total balance of the `delegatee`. + /// + /// This takes into account any slashes reported to `Delegatee` but unapplied. pub(crate) fn effective_balance(&self) -> BalanceOf { defensive_assert!( self.total_delegated >= self.pending_slash, @@ -144,7 +149,7 @@ impl DelegateeLedger { self.total_delegated.saturating_sub(self.pending_slash) } - /// Balance that can be bonded in [`T::CoreStaking`]. + /// Delegatee balance that can be staked/bonded in [`T::CoreStaking`]. pub(crate) fn stakeable_balance(&self) -> BalanceOf { self.effective_balance().saturating_sub(self.unclaimed_withdrawals) } @@ -153,11 +158,14 @@ impl DelegateeLedger { /// Wrapper around `DelegateeLedger` to provide additional functionality. #[derive(Clone)] pub struct Delegatee { + /// storage key pub key: T::AccountId, + /// storage value pub ledger: DelegateeLedger, } impl Delegatee { + /// Get `Delegatee` from storage if it exists or return an error. pub(crate) fn from(delegatee: &T::AccountId) -> Result, DispatchError> { let ledger = DelegateeLedger::::get(delegatee).ok_or(Error::::NotDelegatee)?; Ok(Delegatee { key: delegatee.clone(), ledger }) @@ -212,12 +220,6 @@ impl Delegatee { }) } - /// Reloads self from storage. - #[allow(unused)] - pub(crate) fn refresh(&self) -> Result, DispatchError> { - Self::from(&self.key) - } - /// Amount that is delegated but not bonded yet. /// /// This importantly does not include `unclaimed_withdrawals` as those should not be bonded @@ -234,25 +236,7 @@ impl Delegatee { stakeable.saturating_sub(bonded_stake) } - /// Balance of `Delegatee` that is not bonded. - /// - /// This is similar to [Self::available_to_bond] except it also includes `unclaimed_withdrawals` - /// of `Delegatee`. - #[cfg(test)] - pub(crate) fn total_unbonded(&self) -> BalanceOf { - let bonded_stake = self.bonded_stake(); - - let net_balance = self.ledger.effective_balance(); - - defensive_assert!( - net_balance >= bonded_stake, - "cannot be bonded with more than the delegatee balance" - ); - - net_balance.saturating_sub(bonded_stake) - } - - /// Remove slashes that are applied. + /// Remove slashes from the `DelegateeLedger`. pub(crate) fn remove_slash(self, amount: BalanceOf) -> Self { let pending_slash = self.ledger.pending_slash.defensive_saturating_sub(amount); let total_delegated = self.ledger.total_delegated.defensive_saturating_sub(amount); @@ -263,22 +247,22 @@ impl Delegatee { } } + /// Get the total stake of delegatee bonded in [`Config::CoreStaking`]. pub(crate) fn bonded_stake(&self) -> BalanceOf { T::CoreStaking::total_stake(&self.key).unwrap_or(Zero::zero()) } + /// Returns true if the delegatee is bonded in [`Config::CoreStaking`]. pub(crate) fn is_bonded(&self) -> bool { T::CoreStaking::stake(&self.key).is_ok() } + /// Returns the reward account registered by the delegatee. pub(crate) fn reward_account(&self) -> &T::AccountId { &self.ledger.payee } - pub(crate) fn update_status(self, block: bool) -> Self { - Delegatee { ledger: DelegateeLedger { blocked: block, ..self.ledger }, ..self } - } - + /// Save self to storage. pub(crate) fn save(self) { let key = self.key; self.ledger.save(&key) @@ -303,4 +287,28 @@ impl Delegatee { Ok(()) } + + /// Reloads self from storage. + #[cfg(test)] + pub(crate) fn refresh(&self) -> Result, DispatchError> { + Self::from(&self.key) + } + + /// Balance of `Delegatee` that is not bonded. + /// + /// This is similar to [Self::available_to_bond] except it also includes `unclaimed_withdrawals` + /// of `Delegatee`. + #[cfg(test)] + pub(crate) fn total_unbonded(&self) -> BalanceOf { + let bonded_stake = self.bonded_stake(); + + let net_balance = self.ledger.effective_balance(); + + defensive_assert!( + net_balance >= bonded_stake, + "cannot be bonded with more than the delegatee balance" + ); + + net_balance.saturating_sub(bonded_stake) + } } From 25edbdee291a7346467954d737c64c74a9b3bdb8 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 1 Mar 2024 04:15:06 +0100 Subject: [PATCH 191/202] refactor --- substrate/frame/delegated-staking/src/lib.rs | 4 ++-- substrate/frame/nomination-pools/src/adapter.rs | 6 +++--- substrate/primitives/staking/src/delegation.rs | 12 +++++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 06086390bf85..60eabb005c99 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -84,8 +84,8 @@ //! its Staking provider to support delegation based staking from pool accounts. //! //! #### [Delegatee Support](DelegateeSupport) -//! Implements `DelegateeSupport` trait which another pallet such as [Config::CoreStaking] can use -//! to back-support `Delegatees` in their staking implementation. +//! Implements `DelegateeSupport` trait which an implementation of [StakingInterface] (such as +//! pallet-staking) can use to back-support `delegatee` accounts. //! //! ## Lazy Slashing //! One of the reasons why direct nominators on staking pallet cannot scale well is because all diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index c7bf6868e953..09c8fc488ad7 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -78,8 +78,8 @@ pub trait StakeStrategy { /// A staking strategy implementation that supports transfer based staking. /// -/// In order to stake, this adapter transfers the funds from the delegator account to the pool -/// account and stakes directly on `Staking`. +/// In order to stake, this adapter transfers the funds from the member/delegator account to the +/// pool account and stakes through the pool account on `Staking`. pub struct TransferStake(PhantomData<(T, Staking)>); impl, AccountId = T::AccountId>> @@ -192,7 +192,7 @@ impl, AccountId = T: /// /// In this approach, first the funds are delegated from delegator to the pool account and later /// staked with `Staking`. The advantage of this approach is that the funds are held in the -/// use account itself and not in the pool account. +/// user account itself and not in the pool account. pub struct DelegateStake(PhantomData<(T, Staking)>); impl< diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 7d970d567d9e..6c893bfec70f 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -21,11 +21,11 @@ use scale_info::TypeInfo; use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; -/// A trait that can be used as a plugin to support delegation based accounts, called `Delegatee`. +/// Support plugin for `delegatee` accounts. /// -/// For example, `pallet-staking` which implements `StakingInterface` but does not implement -/// account delegations out of the box can be provided with a custom implementation of this trait to -/// learn how to handle these special accounts. +/// A `delegatee` account is an account that can receive delegations from other accounts. Their +/// balance is made up of multiple child delegators. This trait allows a pallet such as +/// `pallet-staking` to support these special accounts. pub trait DelegateeSupport { /// Balance type used by the staking system. type Balance: Sub @@ -41,7 +41,9 @@ pub trait DelegateeSupport { /// AccountId type used by the staking system. type AccountId: Clone + sp_std::fmt::Debug; - /// Balance of `delegatee` which is available for stake. + /// Balance of `delegatee` which can be staked. + /// + /// Similar to free balance for a normal account. fn stakeable_balance(delegatee: &Self::AccountId) -> Self::Balance; /// Returns true if `delegatee` is restricted to update which account they can receive their From 639985dd779ec7a486963140e2217e0572dd99ee Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 1 Mar 2024 04:15:19 +0100 Subject: [PATCH 192/202] fmt --- substrate/primitives/staking/src/delegation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index 6c893bfec70f..e51a48fddf3a 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -102,8 +102,8 @@ pub trait DelegatedStakeInterface: StakingInterface { /// Withdraw or revoke delegation to `delegatee`. /// - /// If there are `delegatee` funds upto `amount` available to withdraw, then those funds would be - /// released to the `delegator` + /// If there are `delegatee` funds upto `amount` available to withdraw, then those funds would + /// be released to the `delegator` fn withdraw_delegation( delegator: &Self::AccountId, delegatee: &Self::AccountId, From 85682617645fb805a8da82cad9d3a461535442b4 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 21 Mar 2024 10:25:23 +0100 Subject: [PATCH 193/202] fix imports --- Cargo.lock | 1 + substrate/frame/staking/src/ledger.rs | 5 +---- substrate/primitives/staking/Cargo.toml | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61d9823e93b6..fc3a1904e8bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19125,6 +19125,7 @@ dependencies = [ "serde", "sp-core", "sp-runtime", + "sp-std 14.0.0", ] [[package]] diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index c9710e711019..29f19a1b2b8d 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -31,11 +31,8 @@ //! performed through the methods exposed by the [`StakingLedger`] implementation in order to ensure //! state consistency. +use frame_support::{defensive, ensure, traits::Defensive}; use sp_staking::{StakingAccount, StakingInterface}; -use frame_support::{ - defensive, ensure, - traits::{Defensive, LockableCurrency, WithdrawReasons}, -}; use sp_std::prelude::*; use crate::{ diff --git a/substrate/primitives/staking/Cargo.toml b/substrate/primitives/staking/Cargo.toml index 6304551b8e60..21346fbaca53 100644 --- a/substrate/primitives/staking/Cargo.toml +++ b/substrate/primitives/staking/Cargo.toml @@ -23,6 +23,7 @@ impl-trait-for-tuples = "0.2.2" sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", default-features = false } +sp-std = { path = "../std", default-features = false } [features] default = ["std"] @@ -32,5 +33,6 @@ std = [ "serde/std", "sp-core/std", "sp-runtime/std", + "sp-std/std", ] runtime-benchmarks = ["sp-runtime/runtime-benchmarks"] From 2b861f498d8d4c4c2b75ed8045fe67f6c3dd9444 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 28 Mar 2024 13:11:12 +0100 Subject: [PATCH 194/202] merge from master --- .github/workflows/subsystem-benchmarks.yml | 42 ++ .gitlab-ci.yml | 7 + .gitlab/pipeline/publish.yml | 76 ++- .gitlab/pipeline/test.yml | 6 + .gitlab/pipeline/zombienet.yml | 2 +- Cargo.lock | 21 + Cargo.toml | 1 + .../pallets/outbound-queue/src/mock.rs | 1 + bridges/snowbridge/pallets/system/src/mock.rs | 1 + .../consensus/aura/src/collators/lookahead.rs | 39 +- cumulus/pallets/parachain-system/src/mock.rs | 1 + .../chain-specs/coretime-westend.json | 3 +- .../emulated/chains/relays/westend/Cargo.toml | 1 - .../emulated/common/src/impls.rs | 6 +- .../assets/asset-hub-rococo/src/tests/send.rs | 8 +- .../assets/asset-hub-rococo/src/tests/swap.rs | 4 +- .../asset-hub-westend/src/tests/send.rs | 8 +- .../asset-hub-westend/src/tests/swap.rs | 4 +- .../bridge-hub-rococo/src/tests/send_xcm.rs | 7 +- .../bridge-hub-rococo/src/tests/snowbridge.rs | 18 +- .../bridges/bridge-hub-westend/Cargo.toml | 1 + .../bridge-hub-westend/src/tests/send_xcm.rs | 7 +- .../assets/asset-hub-rococo/src/lib.rs | 1 + .../src/weights/pallet_xcm.rs | 112 ++-- .../assets/asset-hub-westend/src/lib.rs | 1 + .../src/weights/pallet_xcm.rs | 114 ++-- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 1 + .../src/weights/pallet_xcm.rs | 108 ++-- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 1 + .../src/weights/pallet_xcm.rs | 108 ++-- .../collectives-westend/src/lib.rs | 1 + .../src/weights/pallet_xcm.rs | 106 ++-- .../contracts/contracts-rococo/src/lib.rs | 1 + .../coretime/coretime-rococo/src/lib.rs | 1 + .../src/weights/pallet_broker.rs | 158 ++--- .../coretime-rococo/src/weights/pallet_xcm.rs | 102 ++-- .../coretime/coretime-westend/src/lib.rs | 1 + .../src/weights/pallet_broker.rs | 160 ++--- .../src/weights/pallet_xcm.rs | 102 ++-- .../glutton/glutton-westend/src/lib.rs | 1 + .../runtimes/people/people-rococo/src/lib.rs | 1 + .../people-rococo/src/weights/pallet_xcm.rs | 102 ++-- .../runtimes/people/people-westend/src/lib.rs | 1 + .../people-westend/src/weights/pallet_xcm.rs | 102 ++-- .../runtimes/starters/shell/src/lib.rs | 1 + .../runtimes/testing/penpal/src/lib.rs | 1 + .../testing/rococo-parachain/src/lib.rs | 1 + .../node/collation-generation/src/error.rs | 2 + polkadot/node/collation-generation/src/lib.rs | 183 +++--- .../node/collation-generation/src/tests.rs | 261 +++++++- polkadot/node/core/approval-voting/src/lib.rs | 6 +- .../approval-voting/src/persisted_entries.rs | 2 +- .../node/core/approval-voting/src/tests.rs | 167 +++++ ...ilability-distribution-regression-bench.rs | 7 + .../availability-recovery-regression-bench.rs | 7 + .../src/collator_side/mod.rs | 129 ++-- .../src/collator_side/tests/mod.rs | 1 + .../tests/prospective_parachains.rs | 2 + .../src/collator_side/validators_buffer.rs | 18 +- polkadot/node/primitives/src/lib.rs | 6 +- polkadot/node/service/Cargo.toml | 3 + polkadot/node/service/src/chain_spec.rs | 7 +- polkadot/node/service/src/fake_runtime_api.rs | 21 +- polkadot/node/subsystem-bench/Cargo.toml | 1 + .../subsystem-bench/src/lib/environment.rs | 2 +- polkadot/node/subsystem-bench/src/lib/lib.rs | 1 + .../node/subsystem-bench/src/lib/usage.rs | 28 + .../node/subsystem-bench/src/lib/utils.rs | 75 +-- polkadot/node/subsystem-types/src/messages.rs | 2 + polkadot/node/subsystem-util/src/lib.rs | 3 +- .../test-parachains/adder/collator/Cargo.toml | 2 +- .../undying/collator/Cargo.toml | 2 +- .../runtime/parachains/src/coretime/mod.rs | 20 + polkadot/runtime/parachains/src/mock.rs | 1 + polkadot/runtime/rococo/Cargo.toml | 2 + polkadot/runtime/rococo/src/impls.rs | 9 +- polkadot/runtime/rococo/src/lib.rs | 66 +- .../runtime/rococo/src/weights/pallet_xcm.rs | 110 ++-- polkadot/runtime/test-runtime/src/lib.rs | 34 +- polkadot/runtime/westend/Cargo.toml | 2 + polkadot/runtime/westend/src/impls.rs | 9 +- polkadot/runtime/westend/src/lib.rs | 64 +- polkadot/runtime/westend/src/tests.rs | 1 - .../westend/src/weights/pallet_staking.rs | 286 +++++---- .../runtime/westend/src/weights/pallet_xcm.rs | 108 ++-- polkadot/xcm/pallet-xcm/Cargo.toml | 2 + polkadot/xcm/pallet-xcm/src/benchmarking.rs | 29 + polkadot/xcm/pallet-xcm/src/lib.rs | 234 +++++-- polkadot/xcm/pallet-xcm/src/tests/mod.rs | 77 ++- polkadot/xcm/src/lib.rs | 13 + polkadot/xcm/src/v4/mod.rs | 20 +- polkadot/xcm/xcm-builder/src/controller.rs | 39 +- polkadot/xcm/xcm-builder/src/lib.rs | 2 +- .../xcm-fee-payment-runtime-api/Cargo.toml | 40 ++ .../xcm-fee-payment-runtime-api/src/lib.rs | 99 +++ .../xcm-simulator/example/src/relay_chain.rs | 1 + .../xcm-simulator/fuzzer/src/relay_chain.rs | 1 + prdoc/pr_3607.prdoc | 26 + prdoc/pr_3706.prdoc | 20 + prdoc/pr_3714.prdoc | 10 + prdoc/pr_3718.prdoc | 13 + prdoc/pr_3749.prdoc | 47 ++ prdoc/pr_3795.prdoc | 14 + prdoc/pr_3817.prdoc | 23 + prdoc/pr_3844.prdoc | 25 + prdoc/pr_3849.prdoc | 13 + prdoc/pr_3850.prdoc | 15 + prdoc/schema_user.json | 8 +- substrate/bin/node/runtime/src/lib.rs | 1 + .../authority-discovery/src/interval.rs | 20 +- .../client/authority-discovery/src/worker.rs | 28 +- substrate/frame/balances/src/impl_currency.rs | 13 +- .../balances/src/tests/currency_tests.rs | 22 +- substrate/frame/broker/src/benchmarking.rs | 17 + .../frame/broker/src/dispatchable_impls.rs | 18 + substrate/frame/broker/src/lib.rs | 8 + substrate/frame/broker/src/weights.rs | 368 ++++++----- .../contracts/mock-network/src/relay_chain.rs | 1 + .../frame/contracts/mock-network/src/tests.rs | 40 +- substrate/frame/contracts/src/lib.rs | 3 + substrate/frame/contracts/src/wasm/runtime.rs | 54 +- substrate/frame/contracts/uapi/src/host.rs | 2 +- .../election-provider-multi-phase/src/lib.rs | 2 - .../message-queue/src/integration_test.rs | 1 + substrate/frame/message-queue/src/lib.rs | 22 +- substrate/frame/message-queue/src/mock.rs | 1 + substrate/frame/message-queue/src/tests.rs | 42 ++ substrate/frame/referenda/src/lib.rs | 12 + substrate/frame/referenda/src/tests.rs | 16 + substrate/frame/scheduler/src/lib.rs | 14 + substrate/frame/scheduler/src/tests.rs | 4 + substrate/frame/staking/Cargo.toml | 2 +- substrate/frame/staking/src/benchmarking.rs | 9 + substrate/frame/staking/src/lib.rs | 14 + substrate/frame/staking/src/mock.rs | 115 ++-- substrate/frame/staking/src/pallet/impls.rs | 60 +- substrate/frame/staking/src/pallet/mod.rs | 121 +++- substrate/frame/staking/src/tests.rs | 446 +++++++++++++- substrate/frame/staking/src/weights.rs | 577 ++++++++++-------- substrate/frame/support/src/traits.rs | 4 +- .../frame/support/src/traits/schedule.rs | 18 +- .../support/src/traits/tokens/currency.rs | 2 +- .../src/traits/tokens/currency/lockable.rs | 6 + .../api/proc-macro/src/impl_runtime_apis.rs | 2 +- templates/parachain/node/src/service.rs | 4 +- templates/parachain/runtime/src/apis.rs | 275 +++++++++ templates/parachain/runtime/src/lib.rs | 238 +------- 147 files changed, 4642 insertions(+), 1933 deletions(-) create mode 100644 .github/workflows/subsystem-benchmarks.yml create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs create mode 100644 prdoc/pr_3607.prdoc create mode 100644 prdoc/pr_3706.prdoc create mode 100644 prdoc/pr_3714.prdoc create mode 100644 prdoc/pr_3718.prdoc create mode 100644 prdoc/pr_3749.prdoc create mode 100644 prdoc/pr_3795.prdoc create mode 100644 prdoc/pr_3817.prdoc create mode 100644 prdoc/pr_3844.prdoc create mode 100644 prdoc/pr_3849.prdoc create mode 100644 prdoc/pr_3850.prdoc create mode 100644 templates/parachain/runtime/src/apis.rs diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml new file mode 100644 index 000000000000..37a9e0f4680c --- /dev/null +++ b/.github/workflows/subsystem-benchmarks.yml @@ -0,0 +1,42 @@ +# The actions takes json file as input and runs github-action-benchmark for it. + +on: + workflow_dispatch: + inputs: + benchmark-data-dir-path: + description: "Path to the benchmark data directory" + required: true + type: string + output-file-path: + description: "Path to the benchmark data file" + required: true + type: string + +jobs: + subsystem-benchmarks: + runs-on: ubuntu-latest + steps: + - name: Checkout Sources + uses: actions/checkout@v4.1.2 + with: + fetch-depth: 0 + ref: "gh-pages" + + - name: Copy bench results + id: step_one + run: | + cp bench/gitlab/${{ github.event.inputs.output-file-path }} ${{ github.event.inputs.output-file-path }} + + - name: Switch branch + id: step_two + run: | + git checkout master + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + output-file-path: ${{ github.event.inputs.output-file-path }} + benchmark-data-dir-path: "bench/${{ github.event.inputs.benchmark-data-dir-path }}" + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f8796ca5124..93a6ccb9f8fb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -147,6 +147,13 @@ default: - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - if: $CI_COMMIT_REF_NAME =~ /^gh-readonly-queue.*$/ # merge queues +.publish-gh-pages-refs: + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME == "master" + # handle the specific case where benches could store incorrect bench data because of the downstream staging runs # exclude cargo-check-benches from such runs .test-refs-check-benches: diff --git a/.gitlab/pipeline/publish.yml b/.gitlab/pipeline/publish.yml index b73acb560f67..a37ba012a8a7 100644 --- a/.gitlab/pipeline/publish.yml +++ b/.gitlab/pipeline/publish.yml @@ -3,16 +3,13 @@ publish-rustdoc: stage: publish - extends: .kubernetes-env + extends: + - .kubernetes-env + - .publish-gh-pages-refs variables: CI_IMAGE: node:18 GIT_DEPTH: 100 RUSTDOCS_DEPLOY_REFS: "master" - rules: - - if: $CI_PIPELINE_SOURCE == "pipeline" - when: never - - if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME == "master" needs: - job: build-rustdoc artifacts: true @@ -60,9 +57,76 @@ publish-rustdoc: - git commit -m "___Updated docs for ${CI_COMMIT_REF_NAME}___" || echo "___Nothing to commit___" - git push origin gh-pages --force + # artificial sleep to publish gh-pages + - sleep 300 + after_script: + - rm -rf .git/ ./* + +publish-subsystem-benchmarks: + stage: publish + variables: + CI_IMAGE: "paritytech/tools:latest" + extends: + - .kubernetes-env + - .publish-gh-pages-refs + needs: + - job: subsystem-regression-tests + artifacts: true + - job: publish-rustdoc + artifacts: false + script: + # setup ssh + - eval $(ssh-agent) + - ssh-add - <<< ${GITHUB_SSH_PRIV_KEY} + - mkdir ~/.ssh && touch ~/.ssh/known_hosts + - ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts + # Set git config + - rm -rf .git/config + - git config user.email "devops-team@parity.io" + - git config user.name "${GITHUB_USER}" + - git config remote.origin.url "git@github.com:/paritytech/${CI_PROJECT_NAME}.git" + - git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" + - git fetch origin gh-pages + # Push result to github + - git checkout gh-pages --force + - mkdir -p bench/gitlab/ || echo "Directory exists" + - rm -rf bench/gitlab/*.json || echo "No json files" + - cp -r charts/*.json bench/gitlab/ + - git add bench/gitlab/ + - git commit -m "Add json files with benchmark results for ${CI_COMMIT_REF_NAME}" + - git push origin gh-pages + # artificial sleep to publish gh-pages + - sleep 300 + allow_failure: true after_script: - rm -rf .git/ ./* +trigger_workflow: + stage: deploy + extends: + - .kubernetes-env + - .publish-gh-pages-refs + needs: + - job: publish-subsystem-benchmarks + artifacts: false + - job: subsystem-regression-tests + artifacts: true + script: + - echo "Triggering workflow" + - | + for benchmark in $(ls charts/*.json); do + export bencmark_name=$(basename $benchmark) + echo "Benchmark: $bencmark_name" + export benchmark_dir=$(echo $bencmark_name | sed 's/\.json//') + curl -q -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + https://api.github.com/repos/paritytech/${CI_PROJECT_NAME}/actions/workflows/subsystem-benchmarks.yml/dispatches \ + -d '{"ref":"refs/heads/master","inputs":{"benchmark-data-dir-path":"'$benchmark_dir'","output-file-path":"'$bencmark_name'"}}' + sleep 300 + done + allow_failure: true + # note: images are used not only in zombienet but also in rococo, wococo and versi .build-push-image: image: $BUILDAH_IMAGE diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 476ac6333f58..af261a893da5 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -497,6 +497,12 @@ test-syscalls: subsystem-regression-tests: stage: test + artifacts: + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: always + expire_in: 1 days + paths: + - charts/ extends: - .docker-env - .common-refs diff --git a/.gitlab/pipeline/zombienet.yml b/.gitlab/pipeline/zombienet.yml index 8d308714fab3..82341eb709fe 100644 --- a/.gitlab/pipeline/zombienet.yml +++ b/.gitlab/pipeline/zombienet.yml @@ -1,7 +1,7 @@ .zombienet-refs: extends: .build-refs variables: - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.95" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.98" include: # substrate tests diff --git a/Cargo.lock b/Cargo.lock index 133d8ba4cc68..95b7a46db6c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2148,6 +2148,7 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parachains-common", + "parity-scale-codec", "rococo-westend-system-emulated-network", "sp-runtime", "staging-xcm", @@ -11211,6 +11212,7 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -13563,12 +13565,14 @@ dependencies = [ "sp-transaction-pool", "sp-version", "sp-weights", + "staging-xcm", "substrate-prometheus-endpoint", "tempfile", "thiserror", "tracing-gum", "westend-runtime", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -13667,6 +13671,7 @@ dependencies = [ "sc-service", "schnorrkel 0.11.4", "serde", + "serde_json", "serde_yaml", "sha1", "sp-application-crypto", @@ -15138,6 +15143,7 @@ dependencies = [ "substrate-wasm-builder", "tiny-keccak", "tokio", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -21993,6 +21999,7 @@ dependencies = [ "tiny-keccak", "tokio", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -22466,6 +22473,20 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "xcm-fee-payment-runtime-api" +version = "0.1.0" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std 14.0.0", + "sp-weights", + "staging-xcm", +] + [[package]] name = "xcm-procedural" version = "7.0.0" diff --git a/Cargo.toml b/Cargo.toml index 5d6faf4ff17c..2966e45fbec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -214,6 +214,7 @@ members = [ "polkadot/xcm/xcm-builder", "polkadot/xcm/xcm-executor", "polkadot/xcm/xcm-executor/integration-tests", + "polkadot/xcm/xcm-fee-payment-runtime-api", "polkadot/xcm/xcm-simulator", "polkadot/xcm/xcm-simulator/example", "polkadot/xcm/xcm-simulator/fuzzer", diff --git a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs index 67877a05c79a..5eeeeead1400 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -69,6 +69,7 @@ impl pallet_message_queue::Config for Test { type HeapSize = HeapSize; type MaxStale = MaxStale; type ServiceWeight = ServiceWeight; + type IdleMaxServiceWeight = (); type QueuePausedQuery = (); } diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 0312456c9823..687072a49e2e 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -148,6 +148,7 @@ impl pallet_message_queue::Config for Test { type HeapSize = HeapSize; type MaxStale = MaxStale; type ServiceWeight = ServiceWeight; + type IdleMaxServiceWeight = (); type QueuePausedQuery = (); } diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index 161f10d55a19..580058336174 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -49,7 +49,7 @@ use polkadot_node_subsystem::messages::{ CollationGenerationMessage, RuntimeApiMessage, RuntimeApiRequest, }; use polkadot_overseer::Handle as OverseerHandle; -use polkadot_primitives::{CollatorPair, Id as ParaId, OccupiedCoreAssumption}; +use polkadot_primitives::{CollatorPair, CoreIndex, Id as ParaId, OccupiedCoreAssumption}; use futures::{channel::oneshot, prelude::*}; use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf}; @@ -184,7 +184,15 @@ where while let Some(relay_parent_header) = import_notifications.next().await { let relay_parent = relay_parent_header.hash(); - if !is_para_scheduled(relay_parent, params.para_id, &mut params.overseer_handle).await { + // TODO: Currently we use just the first core here, but for elastic scaling + // we iterate and build on all of the cores returned. + let core_index = if let Some(core_index) = + cores_scheduled_for_para(relay_parent, params.para_id, &mut params.overseer_handle) + .await + .get(0) + { + *core_index + } else { tracing::trace!( target: crate::LOG_TARGET, ?relay_parent, @@ -193,7 +201,7 @@ where ); continue - } + }; let max_pov_size = match params .relay_client @@ -396,6 +404,7 @@ where parent_head: parent_header.encode().into(), validation_code_hash, result_sender: None, + core_index, }, ), "SubmitCollation", @@ -480,14 +489,12 @@ async fn max_ancestry_lookback( } } -// Checks if there exists a scheduled core for the para at the provided relay parent. -// -// Falls back to `false` in case of an error. -async fn is_para_scheduled( +// Return all the cores assigned to the para at the provided relay parent. +async fn cores_scheduled_for_para( relay_parent: PHash, para_id: ParaId, overseer_handle: &mut OverseerHandle, -) -> bool { +) -> Vec { let (tx, rx) = oneshot::channel(); let request = RuntimeApiRequest::AvailabilityCores(tx); overseer_handle @@ -503,7 +510,7 @@ async fn is_para_scheduled( ?relay_parent, "Failed to query availability cores runtime API", ); - return false + return Vec::new() }, Err(oneshot::Canceled) => { tracing::error!( @@ -511,9 +518,19 @@ async fn is_para_scheduled( ?relay_parent, "Sender for availability cores runtime request dropped", ); - return false + return Vec::new() }, }; - cores.iter().any(|core| core.para_id() == Some(para_id)) + cores + .iter() + .enumerate() + .filter_map(|(index, core)| { + if core.para_id() == Some(para_id) { + Some(CoreIndex(index as u32)) + } else { + None + } + }) + .collect() } diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index 0b1d536ba7cd..fe89dfe68c67 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -125,6 +125,7 @@ impl pallet_message_queue::Config for Test { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MaxWeight; + type IdleMaxServiceWeight = (); type WeightInfo = (); } diff --git a/cumulus/parachains/chain-specs/coretime-westend.json b/cumulus/parachains/chain-specs/coretime-westend.json index c79fd582348b..adb35b8a349f 100644 --- a/cumulus/parachains/chain-specs/coretime-westend.json +++ b/cumulus/parachains/chain-specs/coretime-westend.json @@ -4,7 +4,8 @@ "chainType": "Live", "bootNodes": [ "/dns/westend-coretime-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP93Dzk8T7GWxyWw9jhLcz8Pksokk3R9vL2eEH337bNkT", - "/dns/westend-coretime-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH" + "/dns/westend-coretime-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH", + "/dns/boot.metaspan.io/tcp/33019/p2p/12D3KooWCa1uNnEZqiqJY9jkKNQxwSLGPeZ5MjWHhjQMGwga9JMM" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml index 20aedb50e6a1..12a3ad60e0e0 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml @@ -11,7 +11,6 @@ publish = false workspace = true [dependencies] - # Substrate sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs index ae69bf991e5a..618c3addc5d0 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -362,7 +362,7 @@ macro_rules! impl_send_transact_helpers_for_relay_chain { recipient: $crate::impls::ParaId, call: $crate::impls::DoubleEncoded<()> ) { - use $crate::impls::{bx, Chain, RelayChain}; + use $crate::impls::{bx, Chain, RelayChain, Encode}; ::execute_with(|| { let root_origin = ::RuntimeOrigin::root(); @@ -370,10 +370,10 @@ macro_rules! impl_send_transact_helpers_for_relay_chain { let xcm = $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Superuser); // Send XCM `Transact` - $crate::impls::assert_ok!(]>::XcmPallet::send( + $crate::impls::assert_ok!(]>::XcmPallet::send_blob( root_origin, bx!(destination.into()), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); Self::assert_xcm_pallet_sent(); }); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index 364fbd0d439f..1d120f1dc4c7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { )]); PenpalA::execute_with(|| { - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( root_origin, bx!(system_para_destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); @@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { )]); PenpalA::execute_with(|| { - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( root_origin, bx!(system_para_destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index 87f0b3d9f90a..e13300b7c114 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -370,10 +370,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { penpal.clone(), ); - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( penpal_root, bx!(asset_hub_location), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index eb0e985cc0ce..f218b539c387 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -75,10 +75,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { )]); PenpalA::execute_with(|| { - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( root_origin, bx!(system_para_destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); @@ -159,10 +159,10 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { )]); PenpalA::execute_with(|| { - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( root_origin, bx!(system_para_destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index 04740d311583..aa673c03483a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -369,10 +369,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { penpal.clone(), ); - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( penpal_root, bx!(asset_hub_location), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index a1d871cdb618..4bd041dc03f4 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs @@ -14,6 +14,7 @@ // limitations under the License. use crate::tests::*; +use codec::Encode; #[test] fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable() { @@ -26,7 +27,7 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable let remote_xcm = Xcm(vec![ClearOrigin]); - let xcm = VersionedXcm::from(Xcm(vec![ + let xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { network: WestendId.into(), @@ -38,10 +39,10 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 26b82375e07f..caaf24e00a8a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -83,7 +83,7 @@ fn create_agent() { let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); // Construct XCM to create an agent for para 1001 - let remote_xcm = VersionedXcm::from(Xcm(vec![ + let remote_xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), Transact { @@ -96,10 +96,10 @@ fn create_agent() { // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(remote_xcm), + remote_xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; @@ -141,7 +141,7 @@ fn create_channel() { let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); // Construct XCM to create an agent for para 1001 - let create_agent_xcm = VersionedXcm::from(Xcm(vec![ + let create_agent_xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), Transact { @@ -154,7 +154,7 @@ fn create_channel() { let create_channel_call = SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal }); // Construct XCM to create a channel for para 1001 - let create_channel_xcm = VersionedXcm::from(Xcm(vec![ + let create_channel_xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), Transact { @@ -167,16 +167,16 @@ fn create_channel() { // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin.clone(), bx!(destination.clone()), - bx!(create_agent_xcm), + create_agent_xcm.encode().try_into().unwrap(), )); - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(create_channel_xcm), + create_channel_xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index 9059d841a489..9c45a7adeb4e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -11,6 +11,7 @@ publish = false workspace = true [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.0" } # Substrate frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs index b01be5e8dc84..f69747c17704 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs @@ -14,6 +14,7 @@ // limitations under the License. use crate::tests::*; +use codec::Encode; #[test] fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable() { @@ -26,7 +27,7 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable let remote_xcm = Xcm(vec![ClearOrigin]); - let xcm = VersionedXcm::from(Xcm(vec![ + let xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { network: RococoId, @@ -38,10 +39,10 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable // Westend Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Westend::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 689d8d56c48b..293416ab2a9a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -660,6 +660,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl parachain_info::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs index 51b6543bae82..e0e231d7da27 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 22_136_000 picoseconds. - Weight::from_parts(22_518_000, 0) + // Minimum execution time: 21_224_000 picoseconds. + Weight::from_parts(21_821_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 21_474_000 picoseconds. + Weight::from_parts(22_072_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 92_277_000 picoseconds. - Weight::from_parts(94_843_000, 0) + // Minimum execution time: 90_677_000 picoseconds. + Weight::from_parts(93_658_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -118,8 +140,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `400` // Estimated: `6196` - // Minimum execution time: 120_110_000 picoseconds. - Weight::from_parts(122_968_000, 0) + // Minimum execution time: 116_767_000 picoseconds. + Weight::from_parts(118_843_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) @@ -148,8 +170,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `496` // Estimated: `6208` - // Minimum execution time: 143_116_000 picoseconds. - Weight::from_parts(147_355_000, 0) + // Minimum execution time: 137_983_000 picoseconds. + Weight::from_parts(141_396_000, 0) .saturating_add(Weight::from_parts(0, 6208)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -164,14 +186,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_517_000 picoseconds. - Weight::from_parts(6_756_000, 0) + // Minimum execution time: 6_232_000 picoseconds. + Weight::from_parts(6_507_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -181,8 +213,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_894_000 picoseconds. - Weight::from_parts(2_024_000, 0) + // Minimum execution time: 1_884_000 picoseconds. + Weight::from_parts(2_016_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -208,8 +240,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 27_314_000 picoseconds. - Weight::from_parts(28_787_000, 0) + // Minimum execution time: 26_637_000 picoseconds. + Weight::from_parts(27_616_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -234,8 +266,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 29_840_000 picoseconds. - Weight::from_parts(30_589_000, 0) + // Minimum execution time: 28_668_000 picoseconds. + Weight::from_parts(29_413_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -246,8 +278,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_893_000 picoseconds. - Weight::from_parts(2_017_000, 0) + // Minimum execution time: 1_990_000 picoseconds. + Weight::from_parts(2_114_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -257,8 +289,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `159` // Estimated: `13524` - // Minimum execution time: 19_211_000 picoseconds. - Weight::from_parts(19_552_000, 0) + // Minimum execution time: 18_856_000 picoseconds. + Weight::from_parts(19_430_000, 0) .saturating_add(Weight::from_parts(0, 13524)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -269,8 +301,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `163` // Estimated: `13528` - // Minimum execution time: 19_177_000 picoseconds. - Weight::from_parts(19_704_000, 0) + // Minimum execution time: 19_068_000 picoseconds. + Weight::from_parts(19_434_000, 0) .saturating_add(Weight::from_parts(0, 13528)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -281,8 +313,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `173` // Estimated: `16013` - // Minimum execution time: 20_449_000 picoseconds. - Weight::from_parts(21_075_000, 0) + // Minimum execution time: 21_055_000 picoseconds. + Weight::from_parts(21_379_000, 0) .saturating_add(Weight::from_parts(0, 16013)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -304,8 +336,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 26_578_000 picoseconds. - Weight::from_parts(27_545_000, 0) + // Minimum execution time: 25_736_000 picoseconds. + Weight::from_parts(26_423_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -316,8 +348,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `206` // Estimated: `11096` - // Minimum execution time: 11_646_000 picoseconds. - Weight::from_parts(11_944_000, 0) + // Minimum execution time: 11_853_000 picoseconds. + Weight::from_parts(12_215_000, 0) .saturating_add(Weight::from_parts(0, 11096)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -327,8 +359,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `170` // Estimated: `13535` - // Minimum execution time: 19_301_000 picoseconds. - Weight::from_parts(19_664_000, 0) + // Minimum execution time: 19_418_000 picoseconds. + Weight::from_parts(19_794_000, 0) .saturating_add(Weight::from_parts(0, 13535)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -351,8 +383,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `13577` - // Minimum execution time: 35_715_000 picoseconds. - Weight::from_parts(36_915_000, 0) + // Minimum execution time: 34_719_000 picoseconds. + Weight::from_parts(35_260_000, 0) .saturating_add(Weight::from_parts(0, 13577)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) @@ -365,8 +397,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 4_871_000 picoseconds. - Weight::from_parts(5_066_000, 0) + // Minimum execution time: 4_937_000 picoseconds. + Weight::from_parts(5_203_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -377,8 +409,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 25_150_000 picoseconds. - Weight::from_parts(26_119_000, 0) + // Minimum execution time: 26_064_000 picoseconds. + Weight::from_parts(26_497_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -389,8 +421,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 38_248_000 picoseconds. - Weight::from_parts(39_122_000, 0) + // Minimum execution time: 37_132_000 picoseconds. + Weight::from_parts(37_868_000, 0) .saturating_add(Weight::from_parts(0, 3625)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 48106b5f302d..e92e801e9f52 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -641,6 +641,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs index 71facff87d35..299e4b8b3cd1 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 21_630_000 picoseconds. - Weight::from_parts(22_306_000, 0) + // Minimum execution time: 21_722_000 picoseconds. + Weight::from_parts(22_253_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 21_694_000 picoseconds. + Weight::from_parts(22_326_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 91_802_000 picoseconds. - Weight::from_parts(93_672_000, 0) + // Minimum execution time: 94_422_000 picoseconds. + Weight::from_parts(96_997_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -118,8 +140,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `367` // Estimated: `6196` - // Minimum execution time: 118_930_000 picoseconds. - Weight::from_parts(122_306_000, 0) + // Minimum execution time: 123_368_000 picoseconds. + Weight::from_parts(125_798_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) @@ -148,8 +170,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `496` // Estimated: `6208` - // Minimum execution time: 140_527_000 picoseconds. - Weight::from_parts(144_501_000, 0) + // Minimum execution time: 142_033_000 picoseconds. + Weight::from_parts(145_702_000, 0) .saturating_add(Weight::from_parts(0, 6208)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -158,8 +180,16 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_556_000 picoseconds. - Weight::from_parts(7_798_000, 0) + // Minimum execution time: 7_558_000 picoseconds. + Weight::from_parts(7_916_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_978_000 picoseconds. + Weight::from_parts(8_210_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) @@ -168,8 +198,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_373_000 picoseconds. - Weight::from_parts(6_603_000, 0) + // Minimum execution time: 6_439_000 picoseconds. + Weight::from_parts(6_711_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +209,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_941_000 picoseconds. - Weight::from_parts(2_088_000, 0) + // Minimum execution time: 1_982_000 picoseconds. + Weight::from_parts(2_260_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -206,8 +236,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 27_080_000 picoseconds. - Weight::from_parts(27_820_000, 0) + // Minimum execution time: 27_120_000 picoseconds. + Weight::from_parts(28_048_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -232,8 +262,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 28_850_000 picoseconds. - Weight::from_parts(29_506_000, 0) + // Minimum execution time: 29_354_000 picoseconds. + Weight::from_parts(30_205_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -244,8 +274,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_033_000 picoseconds. - Weight::from_parts(2_201_000, 0) + // Minimum execution time: 1_926_000 picoseconds. + Weight::from_parts(2_013_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -255,8 +285,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `159` // Estimated: `13524` - // Minimum execution time: 18_844_000 picoseconds. - Weight::from_parts(19_197_000, 0) + // Minimum execution time: 18_611_000 picoseconds. + Weight::from_parts(19_120_000, 0) .saturating_add(Weight::from_parts(0, 13524)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -267,8 +297,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `163` // Estimated: `13528` - // Minimum execution time: 18_940_000 picoseconds. - Weight::from_parts(19_450_000, 0) + // Minimum execution time: 18_373_000 picoseconds. + Weight::from_parts(18_945_000, 0) .saturating_add(Weight::from_parts(0, 13528)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -279,8 +309,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `173` // Estimated: `16013` - // Minimum execution time: 20_521_000 picoseconds. - Weight::from_parts(21_076_000, 0) + // Minimum execution time: 20_459_000 picoseconds. + Weight::from_parts(20_951_000, 0) .saturating_add(Weight::from_parts(0, 16013)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -302,8 +332,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 26_007_000 picoseconds. - Weight::from_parts(26_448_000, 0) + // Minimum execution time: 26_003_000 picoseconds. + Weight::from_parts(26_678_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -314,8 +344,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `206` // Estimated: `11096` - // Minimum execution time: 11_584_000 picoseconds. - Weight::from_parts(12_080_000, 0) + // Minimum execution time: 11_557_000 picoseconds. + Weight::from_parts(11_868_000, 0) .saturating_add(Weight::from_parts(0, 11096)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -325,8 +355,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `170` // Estimated: `13535` - // Minimum execution time: 19_157_000 picoseconds. - Weight::from_parts(19_513_000, 0) + // Minimum execution time: 18_710_000 picoseconds. + Weight::from_parts(19_240_000, 0) .saturating_add(Weight::from_parts(0, 13535)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -349,8 +379,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `13577` - // Minimum execution time: 34_878_000 picoseconds. - Weight::from_parts(35_623_000, 0) + // Minimum execution time: 34_393_000 picoseconds. + Weight::from_parts(35_138_000, 0) .saturating_add(Weight::from_parts(0, 13577)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) @@ -363,8 +393,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 3_900_000 picoseconds. - Weight::from_parts(4_161_000, 0) + // Minimum execution time: 4_043_000 picoseconds. + Weight::from_parts(4_216_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -375,8 +405,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 25_731_000 picoseconds. - Weight::from_parts(26_160_000, 0) + // Minimum execution time: 25_410_000 picoseconds. + Weight::from_parts(26_019_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -387,8 +417,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 37_251_000 picoseconds. - Weight::from_parts(38_075_000, 0) + // Minimum execution time: 38_850_000 picoseconds. + Weight::from_parts(39_593_000, 0) .saturating_add(Weight::from_parts(0, 3625)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 3980fa0d501a..f0aa4f8e91cc 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -387,6 +387,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs index a732e1a57343..adfaa9ea2028 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 18_513_000 picoseconds. - Weight::from_parts(19_156_000, 0) + // Minimum execution time: 18_732_000 picoseconds. + Weight::from_parts(19_386_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 18_943_000 picoseconds. + Weight::from_parts(19_455_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 88_096_000 picoseconds. - Weight::from_parts(89_732_000, 0) + // Minimum execution time: 88_917_000 picoseconds. + Weight::from_parts(91_611_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 88_239_000 picoseconds. - Weight::from_parts(89_729_000, 0) + // Minimum execution time: 88_587_000 picoseconds. + Weight::from_parts(90_303_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -142,14 +164,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_955_000 picoseconds. - Weight::from_parts(6_266_000, 0) + // Minimum execution time: 5_856_000 picoseconds. + Weight::from_parts(6_202_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +191,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_868_000 picoseconds. - Weight::from_parts(1_961_000, 0) + // Minimum execution time: 1_797_000 picoseconds. + Weight::from_parts(1_970_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 24_388_000 picoseconds. - Weight::from_parts(25_072_000, 0) + // Minimum execution time: 24_479_000 picoseconds. + Weight::from_parts(25_058_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +244,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 26_762_000 picoseconds. - Weight::from_parts(27_631_000, 0) + // Minimum execution time: 27_282_000 picoseconds. + Weight::from_parts(27_924_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,8 +256,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_856_000 picoseconds. - Weight::from_parts(2_033_000, 0) + // Minimum execution time: 1_801_000 picoseconds. + Weight::from_parts(1_988_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -235,8 +267,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 17_718_000 picoseconds. - Weight::from_parts(18_208_000, 0) + // Minimum execution time: 16_509_000 picoseconds. + Weight::from_parts(16_939_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -247,8 +279,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 17_597_000 picoseconds. - Weight::from_parts(18_090_000, 0) + // Minimum execution time: 16_140_000 picoseconds. + Weight::from_parts(16_843_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -259,8 +291,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 19_533_000 picoseconds. - Weight::from_parts(20_164_000, 0) + // Minimum execution time: 18_160_000 picoseconds. + Weight::from_parts(18_948_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -282,8 +314,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 24_958_000 picoseconds. - Weight::from_parts(25_628_000, 0) + // Minimum execution time: 24_409_000 picoseconds. + Weight::from_parts(25_261_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -294,8 +326,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 12_209_000 picoseconds. - Weight::from_parts(12_612_000, 0) + // Minimum execution time: 10_848_000 picoseconds. + Weight::from_parts(11_241_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -305,8 +337,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 17_844_000 picoseconds. - Weight::from_parts(18_266_000, 0) + // Minimum execution time: 16_609_000 picoseconds. + Weight::from_parts(17_044_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -329,8 +361,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `13471` - // Minimum execution time: 34_131_000 picoseconds. - Weight::from_parts(34_766_000, 0) + // Minimum execution time: 32_500_000 picoseconds. + Weight::from_parts(33_475_000, 0) .saturating_add(Weight::from_parts(0, 13471)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) @@ -343,8 +375,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_525_000 picoseconds. - Weight::from_parts(3_724_000, 0) + // Minimum execution time: 3_484_000 picoseconds. + Weight::from_parts(3_673_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,8 +387,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 24_975_000 picoseconds. - Weight::from_parts(25_517_000, 0) + // Minimum execution time: 25_225_000 picoseconds. + Weight::from_parts(25_731_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -367,8 +399,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 33_761_000 picoseconds. - Weight::from_parts(34_674_000, 0) + // Minimum execution time: 33_961_000 picoseconds. + Weight::from_parts(34_818_000, 0) .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 9bdea6b9a7dd..3b759301d0ed 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -348,6 +348,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs index a78ff2355efa..9cf4c61466a1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 19_527_000 picoseconds. - Weight::from_parts(19_839_000, 0) + // Minimum execution time: 19_702_000 picoseconds. + Weight::from_parts(20_410_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 19_525_000 picoseconds. + Weight::from_parts(20_071_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `107` // Estimated: `3593` - // Minimum execution time: 90_938_000 picoseconds. - Weight::from_parts(92_822_000, 0) + // Minimum execution time: 91_793_000 picoseconds. + Weight::from_parts(93_761_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `107` // Estimated: `3593` - // Minimum execution time: 90_133_000 picoseconds. - Weight::from_parts(92_308_000, 0) + // Minimum execution time: 91_819_000 picoseconds. + Weight::from_parts(93_198_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -142,14 +164,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_205_000 picoseconds. - Weight::from_parts(6_595_000, 0) + // Minimum execution time: 6_183_000 picoseconds. + Weight::from_parts(6_598_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +191,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_927_000 picoseconds. - Weight::from_parts(2_062_000, 0) + // Minimum execution time: 1_987_000 picoseconds. + Weight::from_parts(2_076_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_078_000 picoseconds. - Weight::from_parts(25_782_000, 0) + // Minimum execution time: 25_375_000 picoseconds. + Weight::from_parts(26_165_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +244,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 28_188_000 picoseconds. - Weight::from_parts(28_826_000, 0) + // Minimum execution time: 28_167_000 picoseconds. + Weight::from_parts(28_792_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,8 +256,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_886_000 picoseconds. - Weight::from_parts(1_991_000, 0) + // Minimum execution time: 2_039_000 picoseconds. + Weight::from_parts(2_211_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -235,8 +267,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 17_443_000 picoseconds. - Weight::from_parts(17_964_000, 0) + // Minimum execution time: 17_127_000 picoseconds. + Weight::from_parts(17_519_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -247,8 +279,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 17_357_000 picoseconds. - Weight::from_parts(18_006_000, 0) + // Minimum execution time: 16_701_000 picoseconds. + Weight::from_parts(17_250_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -259,8 +291,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 18_838_000 picoseconds. - Weight::from_parts(19_688_000, 0) + // Minimum execution time: 18_795_000 picoseconds. + Weight::from_parts(19_302_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -282,8 +314,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 25_517_000 picoseconds. - Weight::from_parts(26_131_000, 0) + // Minimum execution time: 25_007_000 picoseconds. + Weight::from_parts(25_786_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -294,8 +326,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 11_587_000 picoseconds. - Weight::from_parts(11_963_000, 0) + // Minimum execution time: 11_534_000 picoseconds. + Weight::from_parts(11_798_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -305,8 +337,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 17_490_000 picoseconds. - Weight::from_parts(18_160_000, 0) + // Minimum execution time: 17_357_000 picoseconds. + Weight::from_parts(17_629_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -329,8 +361,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `13471` - // Minimum execution time: 34_088_000 picoseconds. - Weight::from_parts(34_598_000, 0) + // Minimum execution time: 33_487_000 picoseconds. + Weight::from_parts(34_033_000, 0) .saturating_add(Weight::from_parts(0, 13471)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) @@ -343,8 +375,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_566_000 picoseconds. - Weight::from_parts(3_754_000, 0) + // Minimum execution time: 3_688_000 picoseconds. + Weight::from_parts(3_854_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,8 +387,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 25_078_000 picoseconds. - Weight::from_parts(25_477_000, 0) + // Minimum execution time: 26_336_000 picoseconds. + Weight::from_parts(26_873_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -367,8 +399,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 34_661_000 picoseconds. - Weight::from_parts(35_411_000, 0) + // Minimum execution time: 34_633_000 picoseconds. + Weight::from_parts(35_171_000, 0) .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index d3f588bf25ff..e1c2e1a6237b 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -423,6 +423,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs index 5d427d850046..0edd5dfff2b8 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 21_813_000 picoseconds. - Weight::from_parts(22_332_000, 0) + // Minimum execution time: 21_911_000 picoseconds. + Weight::from_parts(22_431_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 22_143_000 picoseconds. + Weight::from_parts(22_843_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3679` - // Minimum execution time: 93_243_000 picoseconds. - Weight::from_parts(95_650_000, 0) + // Minimum execution time: 96_273_000 picoseconds. + Weight::from_parts(98_351_000, 0) .saturating_add(Weight::from_parts(0, 3679)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3679` - // Minimum execution time: 96_199_000 picoseconds. - Weight::from_parts(98_620_000, 0) + // Minimum execution time: 95_571_000 picoseconds. + Weight::from_parts(96_251_000, 0) .saturating_add(Weight::from_parts(0, 3679)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -142,14 +164,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_442_000 picoseconds. - Weight::from_parts(6_682_000, 0) + // Minimum execution time: 6_227_000 picoseconds. + Weight::from_parts(6_419_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +191,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_833_000 picoseconds. - Weight::from_parts(1_973_000, 0) + // Minimum execution time: 1_851_000 picoseconds. + Weight::from_parts(1_940_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 27_318_000 picoseconds. - Weight::from_parts(28_224_000, 0) + // Minimum execution time: 27_449_000 picoseconds. + Weight::from_parts(28_513_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +244,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 29_070_000 picoseconds. - Weight::from_parts(30_205_000, 0) + // Minimum execution time: 29_477_000 picoseconds. + Weight::from_parts(30_251_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,8 +256,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_904_000 picoseconds. - Weight::from_parts(2_033_000, 0) + // Minimum execution time: 1_894_000 picoseconds. + Weight::from_parts(2_009_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -235,8 +267,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `159` // Estimated: `13524` - // Minimum execution time: 18_348_000 picoseconds. - Weight::from_parts(18_853_000, 0) + // Minimum execution time: 17_991_000 picoseconds. + Weight::from_parts(18_651_000, 0) .saturating_add(Weight::from_parts(0, 13524)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -247,8 +279,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `163` // Estimated: `13528` - // Minimum execution time: 17_964_000 picoseconds. - Weight::from_parts(18_548_000, 0) + // Minimum execution time: 18_321_000 picoseconds. + Weight::from_parts(18_701_000, 0) .saturating_add(Weight::from_parts(0, 13528)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -259,8 +291,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `173` // Estimated: `16013` - // Minimum execution time: 19_708_000 picoseconds. - Weight::from_parts(20_157_000, 0) + // Minimum execution time: 19_762_000 picoseconds. + Weight::from_parts(20_529_000, 0) .saturating_add(Weight::from_parts(0, 16013)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -282,8 +314,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 26_632_000 picoseconds. - Weight::from_parts(27_314_000, 0) + // Minimum execution time: 26_927_000 picoseconds. + Weight::from_parts(27_629_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -294,8 +326,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `206` // Estimated: `11096` - // Minimum execution time: 11_929_000 picoseconds. - Weight::from_parts(12_304_000, 0) + // Minimum execution time: 11_957_000 picoseconds. + Weight::from_parts(12_119_000, 0) .saturating_add(Weight::from_parts(0, 11096)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -305,8 +337,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `170` // Estimated: `13535` - // Minimum execution time: 18_599_000 picoseconds. - Weight::from_parts(19_195_000, 0) + // Minimum execution time: 17_942_000 picoseconds. + Weight::from_parts(18_878_000, 0) .saturating_add(Weight::from_parts(0, 13535)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -329,8 +361,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `13577` - // Minimum execution time: 35_524_000 picoseconds. - Weight::from_parts(36_272_000, 0) + // Minimum execution time: 35_640_000 picoseconds. + Weight::from_parts(36_340_000, 0) .saturating_add(Weight::from_parts(0, 13577)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) @@ -344,7 +376,7 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Measured: `103` // Estimated: `1588` // Minimum execution time: 4_044_000 picoseconds. - Weight::from_parts(4_238_000, 0) + Weight::from_parts(4_229_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,8 +387,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 25_741_000 picoseconds. - Weight::from_parts(26_301_000, 0) + // Minimum execution time: 26_262_000 picoseconds. + Weight::from_parts(26_842_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -367,8 +399,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 35_925_000 picoseconds. - Weight::from_parts(36_978_000, 0) + // Minimum execution time: 36_775_000 picoseconds. + Weight::from_parts(37_265_000, 0) .saturating_add(Weight::from_parts(0, 3625)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index e1586c7d9b29..ec0a5f6fc96c 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -318,6 +318,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 86eb5cdfcaf5..67f486893532 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -301,6 +301,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl parachain_info::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs index 2d30ddc612cb..89b1c4c86632 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_broker` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_broker -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_broker +// --chain=coretime-rococo-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ @@ -56,8 +54,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_462_000 picoseconds. - Weight::from_parts(2_552_000, 0) + // Minimum execution time: 1_918_000 picoseconds. + Weight::from_parts(2_092_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -67,8 +65,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `10888` // Estimated: `13506` - // Minimum execution time: 25_494_000 picoseconds. - Weight::from_parts(26_063_000, 0) + // Minimum execution time: 21_943_000 picoseconds. + Weight::from_parts(22_570_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +77,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `12090` // Estimated: `13506` - // Minimum execution time: 22_299_000 picoseconds. - Weight::from_parts(22_911_000, 0) + // Minimum execution time: 20_923_000 picoseconds. + Weight::from_parts(21_354_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -95,8 +93,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `466` // Estimated: `1951` - // Minimum execution time: 11_590_000 picoseconds. - Weight::from_parts(12_007_000, 0) + // Minimum execution time: 10_687_000 picoseconds. + Weight::from_parts(11_409_000, 0) .saturating_add(Weight::from_parts(0, 1951)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -124,11 +122,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `12567` // Estimated: `14052` - // Minimum execution time: 120_928_000 picoseconds. - Weight::from_parts(124_947_252, 0) + // Minimum execution time: 111_288_000 picoseconds. + Weight::from_parts(117_804_282, 0) .saturating_add(Weight::from_parts(0, 14052)) - // Standard Error: 435 - .saturating_add(Weight::from_parts(1_246, 0).saturating_mul(n.into())) + // Standard Error: 391 + .saturating_add(Weight::from_parts(1_243, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(66)) } @@ -144,8 +142,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `316` // Estimated: `3593` - // Minimum execution time: 32_826_000 picoseconds. - Weight::from_parts(33_889_000, 0) + // Minimum execution time: 33_006_000 picoseconds. + Weight::from_parts(34_256_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -166,8 +164,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `434` // Estimated: `4698` - // Minimum execution time: 57_362_000 picoseconds. - Weight::from_parts(58_994_000, 0) + // Minimum execution time: 61_473_000 picoseconds. + Weight::from_parts(66_476_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -178,8 +176,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 13_982_000 picoseconds. - Weight::from_parts(14_447_000, 0) + // Minimum execution time: 13_771_000 picoseconds. + Weight::from_parts(14_374_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -190,8 +188,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 15_070_000 picoseconds. - Weight::from_parts(15_735_000, 0) + // Minimum execution time: 15_162_000 picoseconds. + Weight::from_parts(15_742_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -202,8 +200,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 16_527_000 picoseconds. - Weight::from_parts(16_894_000, 0) + // Minimum execution time: 16_196_000 picoseconds. + Weight::from_parts(16_796_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(3)) @@ -220,8 +218,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `936` // Estimated: `4681` - // Minimum execution time: 25_493_000 picoseconds. - Weight::from_parts(26_091_000, 0) + // Minimum execution time: 25_653_000 picoseconds. + Weight::from_parts(27_006_000, 0) .saturating_add(Weight::from_parts(0, 4681)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -240,8 +238,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1002` // Estimated: `5996` - // Minimum execution time: 31_498_000 picoseconds. - Weight::from_parts(32_560_000, 0) + // Minimum execution time: 31_114_000 picoseconds. + Weight::from_parts(32_235_000, 0) .saturating_add(Weight::from_parts(0, 5996)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(5)) @@ -257,11 +255,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `652` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 57_183_000 picoseconds. - Weight::from_parts(58_024_898, 0) + // Minimum execution time: 57_280_000 picoseconds. + Weight::from_parts(58_127_480, 0) .saturating_add(Weight::from_parts(0, 6196)) - // Standard Error: 35_831 - .saturating_add(Weight::from_parts(1_384_446, 0).saturating_mul(m.into())) + // Standard Error: 41_670 + .saturating_add(Weight::from_parts(1_203_066, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -283,8 +281,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `215` // Estimated: `3680` - // Minimum execution time: 59_762_000 picoseconds. - Weight::from_parts(61_114_000, 0) + // Minimum execution time: 59_968_000 picoseconds. + Weight::from_parts(62_315_000, 0) .saturating_add(Weight::from_parts(0, 3680)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -297,8 +295,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `465` // Estimated: `3550` - // Minimum execution time: 41_473_000 picoseconds. - Weight::from_parts(44_155_000, 0) + // Minimum execution time: 50_887_000 picoseconds. + Weight::from_parts(57_366_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -313,8 +311,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `463` // Estimated: `3533` - // Minimum execution time: 56_672_000 picoseconds. - Weight::from_parts(58_086_000, 0) + // Minimum execution time: 84_472_000 picoseconds. + Weight::from_parts(96_536_000, 0) .saturating_add(Weight::from_parts(0, 3533)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -331,8 +329,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `857` // Estimated: `3593` - // Minimum execution time: 64_460_000 picoseconds. - Weight::from_parts(65_894_000, 0) + // Minimum execution time: 96_371_000 picoseconds. + Weight::from_parts(104_659_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -345,8 +343,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `957` // Estimated: `4698` - // Minimum execution time: 37_447_000 picoseconds. - Weight::from_parts(42_318_000, 0) + // Minimum execution time: 51_741_000 picoseconds. + Weight::from_parts(54_461_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -366,8 +364,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 21_219_000 picoseconds. - Weight::from_parts(22_084_648, 0) + // Minimum execution time: 19_901_000 picoseconds. + Weight::from_parts(21_028_116, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -379,11 +377,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `266` // Estimated: `1487` - // Minimum execution time: 5_792_000 picoseconds. - Weight::from_parts(6_358_588, 0) + // Minimum execution time: 5_987_000 picoseconds. + Weight::from_parts(6_412_478, 0) .saturating_add(Weight::from_parts(0, 1487)) - // Standard Error: 20 - .saturating_add(Weight::from_parts(26, 0).saturating_mul(n.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(47, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -397,8 +395,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `447` // Estimated: `6196` - // Minimum execution time: 38_690_000 picoseconds. - Weight::from_parts(39_706_000, 0) + // Minimum execution time: 38_623_000 picoseconds. + Weight::from_parts(39_773_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -414,15 +412,13 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `Broker::Workplan` (r:0 w:60) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn rotate_sale(n: u32, ) -> Weight { + fn rotate_sale(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12514` // Estimated: `13506` - // Minimum execution time: 93_531_000 picoseconds. - Weight::from_parts(95_836_318, 0) + // Minimum execution time: 97_074_000 picoseconds. + Weight::from_parts(101_247_740, 0) .saturating_add(Weight::from_parts(0, 13506)) - // Standard Error: 113 - .saturating_add(Weight::from_parts(329, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(65)) } @@ -434,8 +430,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3493` - // Minimum execution time: 6_506_000 picoseconds. - Weight::from_parts(6_783_000, 0) + // Minimum execution time: 6_317_000 picoseconds. + Weight::from_parts(6_521_000, 0) .saturating_add(Weight::from_parts(0, 3493)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -458,8 +454,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1321` // Estimated: `4786` - // Minimum execution time: 31_927_000 picoseconds. - Weight::from_parts(32_748_000, 0) + // Minimum execution time: 32_575_000 picoseconds. + Weight::from_parts(33_299_000, 0) .saturating_add(Weight::from_parts(0, 4786)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -478,8 +474,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 15_682_000 picoseconds. - Weight::from_parts(16_012_000, 0) + // Minimum execution time: 15_256_000 picoseconds. + Weight::from_parts(15_927_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -490,8 +486,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_147_000 picoseconds. - Weight::from_parts(2_281_000, 0) + // Minimum execution time: 1_783_000 picoseconds. + Weight::from_parts(1_904_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -509,10 +505,22 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `398` // Estimated: `3863` - // Minimum execution time: 12_015_000 picoseconds. - Weight::from_parts(12_619_000, 0) + // Minimum execution time: 12_307_000 picoseconds. + Weight::from_parts(12_967_000, 0) .saturating_add(Weight::from_parts(0, 3863)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(401), added: 896, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `470` + // Estimated: `1886` + // Minimum execution time: 6_597_000 picoseconds. + Weight::from_parts(6_969_000, 0) + .saturating_add(Weight::from_parts(0, 1886)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs index c5d315467c1e..df0044089c8f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -62,8 +62,28 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 35_051_000 picoseconds. - Weight::from_parts(35_200_000, 0) + // Minimum execution time: 18_767_000 picoseconds. + Weight::from_parts(19_420_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 19_184_000 picoseconds. + Weight::from_parts(19_695_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,8 +104,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 56_235_000 picoseconds. - Weight::from_parts(58_178_000, 0) + // Minimum execution time: 58_120_000 picoseconds. + Weight::from_parts(59_533_000, 0) .saturating_add(Weight::from_parts(0, 3571)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -120,14 +140,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_226_000 picoseconds. - Weight::from_parts(6_403_000, 0) + // Minimum execution time: 6_074_000 picoseconds. + Weight::from_parts(6_398_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -137,8 +167,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_020_000 picoseconds. - Weight::from_parts(2_100_000, 0) + // Minimum execution time: 2_036_000 picoseconds. + Weight::from_parts(2_180_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -162,8 +192,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 24_387_000 picoseconds. - Weight::from_parts(24_814_000, 0) + // Minimum execution time: 25_014_000 picoseconds. + Weight::from_parts(25_374_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -186,8 +216,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `292` // Estimated: `3757` - // Minimum execution time: 27_039_000 picoseconds. - Weight::from_parts(27_693_000, 0) + // Minimum execution time: 27_616_000 picoseconds. + Weight::from_parts(28_499_000, 0) .saturating_add(Weight::from_parts(0, 3757)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -198,8 +228,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_920_000 picoseconds. - Weight::from_parts(2_082_000, 0) + // Minimum execution time: 2_061_000 picoseconds. + Weight::from_parts(2_153_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -209,8 +239,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 17_141_000 picoseconds. - Weight::from_parts(17_500_000, 0) + // Minimum execution time: 16_592_000 picoseconds. + Weight::from_parts(16_900_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -221,8 +251,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 17_074_000 picoseconds. - Weight::from_parts(17_431_000, 0) + // Minimum execution time: 16_694_000 picoseconds. + Weight::from_parts(16_905_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -233,8 +263,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 19_139_000 picoseconds. - Weight::from_parts(19_474_000, 0) + // Minimum execution time: 17_779_000 picoseconds. + Weight::from_parts(18_490_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -254,8 +284,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `6082` - // Minimum execution time: 24_346_000 picoseconds. - Weight::from_parts(25_318_000, 0) + // Minimum execution time: 24_526_000 picoseconds. + Weight::from_parts(25_182_000, 0) .saturating_add(Weight::from_parts(0, 6082)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) @@ -266,8 +296,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 11_777_000 picoseconds. - Weight::from_parts(12_051_000, 0) + // Minimum execution time: 10_467_000 picoseconds. + Weight::from_parts(10_934_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -277,8 +307,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 17_538_000 picoseconds. - Weight::from_parts(17_832_000, 0) + // Minimum execution time: 16_377_000 picoseconds. + Weight::from_parts(17_114_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -299,8 +329,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `13507` - // Minimum execution time: 33_623_000 picoseconds. - Weight::from_parts(34_186_000, 0) + // Minimum execution time: 32_575_000 picoseconds. + Weight::from_parts(33_483_000, 0) .saturating_add(Weight::from_parts(0, 13507)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) @@ -313,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_363_000 picoseconds. - Weight::from_parts(3_511_000, 0) + // Minimum execution time: 3_604_000 picoseconds. + Weight::from_parts(3_744_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -325,8 +355,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 23_969_000 picoseconds. - Weight::from_parts(24_347_000, 0) + // Minimum execution time: 23_983_000 picoseconds. + Weight::from_parts(24_404_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -337,8 +367,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 34_071_000 picoseconds. - Weight::from_parts(35_031_000, 0) + // Minimum execution time: 34_446_000 picoseconds. + Weight::from_parts(35_465_000, 0) .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index c31e474cc2f1..609ea5a38a89 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -301,6 +301,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl parachain_info::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs index 8727b9633b1f..13d5fcf3898b 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs @@ -17,25 +17,23 @@ //! Autogenerated weights for `pallet_broker` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-westend-dev -// --wasm-execution=compiled -// --pallet=pallet_broker -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_broker +// --chain=coretime-westend-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ @@ -56,8 +54,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_944_000 picoseconds. - Weight::from_parts(2_045_000, 0) + // Minimum execution time: 1_897_000 picoseconds. + Weight::from_parts(2_053_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -67,8 +65,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `10888` // Estimated: `13506` - // Minimum execution time: 21_158_000 picoseconds. - Weight::from_parts(21_572_000, 0) + // Minimum execution time: 22_550_000 picoseconds. + Weight::from_parts(22_871_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +77,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `12090` // Estimated: `13506` - // Minimum execution time: 20_497_000 picoseconds. - Weight::from_parts(20_995_000, 0) + // Minimum execution time: 21_170_000 picoseconds. + Weight::from_parts(21_645_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -95,8 +93,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `146` // Estimated: `1631` - // Minimum execution time: 10_280_000 picoseconds. - Weight::from_parts(10_686_000, 0) + // Minimum execution time: 10_494_000 picoseconds. + Weight::from_parts(10_942_000, 0) .saturating_add(Weight::from_parts(0, 1631)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -120,15 +118,13 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `Broker::Workplan` (r:0 w:20) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12247` // Estimated: `13732` - // Minimum execution time: 61_020_000 picoseconds. - Weight::from_parts(63_240_622, 0) + // Minimum execution time: 61_014_000 picoseconds. + Weight::from_parts(63_267_651, 0) .saturating_add(Weight::from_parts(0, 13732)) - // Standard Error: 102 - .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(26)) } @@ -144,8 +140,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `316` // Estimated: `3593` - // Minimum execution time: 30_627_000 picoseconds. - Weight::from_parts(31_648_000, 0) + // Minimum execution time: 30_931_000 picoseconds. + Weight::from_parts(31_941_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -166,8 +162,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `434` // Estimated: `4698` - // Minimum execution time: 57_701_000 picoseconds. - Weight::from_parts(59_825_000, 0) + // Minimum execution time: 57_466_000 picoseconds. + Weight::from_parts(65_042_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -178,8 +174,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 12_898_000 picoseconds. - Weight::from_parts(13_506_000, 0) + // Minimum execution time: 12_799_000 picoseconds. + Weight::from_parts(13_401_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -190,8 +186,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 14_284_000 picoseconds. - Weight::from_parts(14_791_000, 0) + // Minimum execution time: 14_107_000 picoseconds. + Weight::from_parts(14_630_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -202,8 +198,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 15_570_000 picoseconds. - Weight::from_parts(16_158_000, 0) + // Minimum execution time: 15_254_000 picoseconds. + Weight::from_parts(16_062_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(3)) @@ -220,8 +216,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `735` // Estimated: `4681` - // Minimum execution time: 23_329_000 picoseconds. - Weight::from_parts(24_196_000, 0) + // Minimum execution time: 23_557_000 picoseconds. + Weight::from_parts(24_382_000, 0) .saturating_add(Weight::from_parts(0, 4681)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -240,8 +236,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `801` // Estimated: `5996` - // Minimum execution time: 29_288_000 picoseconds. - Weight::from_parts(30_066_000, 0) + // Minimum execution time: 29_371_000 picoseconds. + Weight::from_parts(30_200_000, 0) .saturating_add(Weight::from_parts(0, 5996)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(5)) @@ -257,11 +253,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `652` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 54_833_000 picoseconds. - Weight::from_parts(55_577_423, 0) + // Minimum execution time: 54_331_000 picoseconds. + Weight::from_parts(55_322_165, 0) .saturating_add(Weight::from_parts(0, 6196)) - // Standard Error: 35_105 - .saturating_add(Weight::from_parts(1_267_911, 0).saturating_mul(m.into())) + // Standard Error: 35_225 + .saturating_add(Weight::from_parts(1_099_614, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -283,8 +279,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `215` // Estimated: `3680` - // Minimum execution time: 55_289_000 picoseconds. - Weight::from_parts(56_552_000, 0) + // Minimum execution time: 53_789_000 picoseconds. + Weight::from_parts(55_439_000, 0) .saturating_add(Weight::from_parts(0, 3680)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -297,8 +293,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `465` // Estimated: `3550` - // Minimum execution time: 39_736_000 picoseconds. - Weight::from_parts(41_346_000, 0) + // Minimum execution time: 43_941_000 picoseconds. + Weight::from_parts(49_776_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -313,8 +309,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `463` // Estimated: `3533` - // Minimum execution time: 57_319_000 picoseconds. - Weight::from_parts(60_204_000, 0) + // Minimum execution time: 64_917_000 picoseconds. + Weight::from_parts(70_403_000, 0) .saturating_add(Weight::from_parts(0, 3533)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -331,8 +327,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `857` // Estimated: `3593` - // Minimum execution time: 85_216_000 picoseconds. - Weight::from_parts(91_144_000, 0) + // Minimum execution time: 72_633_000 picoseconds. + Weight::from_parts(79_305_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -345,8 +341,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `556` // Estimated: `4698` - // Minimum execution time: 32_331_000 picoseconds. - Weight::from_parts(39_877_000, 0) + // Minimum execution time: 36_643_000 picoseconds. + Weight::from_parts(48_218_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -362,28 +358,28 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `n` is `[0, 1000]`. - fn request_core_count(n: u32, ) -> Weight { + fn request_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 18_128_000 picoseconds. - Weight::from_parts(19_061_234, 0) + // Minimum execution time: 17_617_000 picoseconds. + Weight::from_parts(18_904_788, 0) .saturating_add(Weight::from_parts(0, 3539)) - // Standard Error: 48 - .saturating_add(Weight::from_parts(141, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Broker::CoreCountInbox` (r:1 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn process_core_count(_n: u32, ) -> Weight { + fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `266` // Estimated: `1487` - // Minimum execution time: 5_368_000 picoseconds. - Weight::from_parts(5_837_005, 0) + // Minimum execution time: 5_575_000 picoseconds. + Weight::from_parts(5_887_598, 0) .saturating_add(Weight::from_parts(0, 1487)) + // Standard Error: 16 + .saturating_add(Weight::from_parts(41, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -397,8 +393,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `447` // Estimated: `6196` - // Minimum execution time: 36_047_000 picoseconds. - Weight::from_parts(37_101_000, 0) + // Minimum execution time: 36_415_000 picoseconds. + Weight::from_parts(37_588_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -414,13 +410,15 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `Broker::Workplan` (r:0 w:20) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn rotate_sale(_n: u32, ) -> Weight { + fn rotate_sale(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12194` // Estimated: `13506` - // Minimum execution time: 48_158_000 picoseconds. - Weight::from_parts(49_891_920, 0) + // Minimum execution time: 48_362_000 picoseconds. + Weight::from_parts(49_616_106, 0) .saturating_add(Weight::from_parts(0, 13506)) + // Standard Error: 61 + .saturating_add(Weight::from_parts(59, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(25)) } @@ -432,8 +430,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3493` - // Minimum execution time: 5_911_000 picoseconds. - Weight::from_parts(6_173_000, 0) + // Minimum execution time: 6_148_000 picoseconds. + Weight::from_parts(6_374_000, 0) .saturating_add(Weight::from_parts(0, 3493)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -456,8 +454,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1321` // Estimated: `4786` - // Minimum execution time: 30_140_000 picoseconds. - Weight::from_parts(30_912_000, 0) + // Minimum execution time: 30_267_000 picoseconds. + Weight::from_parts(30_825_000, 0) .saturating_add(Weight::from_parts(0, 4786)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -476,8 +474,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 13_684_000 picoseconds. - Weight::from_parts(14_252_000, 0) + // Minimum execution time: 13_491_000 picoseconds. + Weight::from_parts(13_949_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -488,8 +486,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_718_000 picoseconds. - Weight::from_parts(1_843_000, 0) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(1_913_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -507,10 +505,22 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `398` // Estimated: `3863` - // Minimum execution time: 11_771_000 picoseconds. - Weight::from_parts(12_120_000, 0) + // Minimum execution time: 12_035_000 picoseconds. + Weight::from_parts(12_383_000, 0) .saturating_add(Weight::from_parts(0, 3863)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(81), added: 576, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `150` + // Estimated: `1566` + // Minimum execution time: 6_142_000 picoseconds. + Weight::from_parts(6_538_000, 0) + .saturating_add(Weight::from_parts(0, 1566)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs index 0082db3099d0..a1701c5f1c2c 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -62,8 +62,28 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 18_410_000 picoseconds. - Weight::from_parts(18_657_000, 0) + // Minimum execution time: 17_681_000 picoseconds. + Weight::from_parts(18_350_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 18_091_000 picoseconds. + Weight::from_parts(18_327_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,8 +104,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 56_616_000 picoseconds. - Weight::from_parts(57_751_000, 0) + // Minimum execution time: 54_943_000 picoseconds. + Weight::from_parts(56_519_000, 0) .saturating_add(Weight::from_parts(0, 3571)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -120,14 +140,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_014_000 picoseconds. - Weight::from_parts(6_412_000, 0) + // Minimum execution time: 5_887_000 picoseconds. + Weight::from_parts(6_101_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -137,8 +167,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_844_000 picoseconds. - Weight::from_parts(1_957_000, 0) + // Minimum execution time: 1_940_000 picoseconds. + Weight::from_parts(2_022_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -162,8 +192,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 24_067_000 picoseconds. - Weight::from_parts(24_553_000, 0) + // Minimum execution time: 23_165_000 picoseconds. + Weight::from_parts(23_800_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -186,8 +216,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `292` // Estimated: `3757` - // Minimum execution time: 27_023_000 picoseconds. - Weight::from_parts(27_620_000, 0) + // Minimum execution time: 26_506_000 picoseconds. + Weight::from_parts(27_180_000, 0) .saturating_add(Weight::from_parts(0, 3757)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -198,8 +228,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_866_000 picoseconds. - Weight::from_parts(1_984_000, 0) + // Minimum execution time: 1_868_000 picoseconds. + Weight::from_parts(2_002_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -209,8 +239,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 16_425_000 picoseconds. - Weight::from_parts(16_680_000, 0) + // Minimum execution time: 16_138_000 picoseconds. + Weight::from_parts(16_447_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -221,8 +251,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 16_171_000 picoseconds. - Weight::from_parts(16_564_000, 0) + // Minimum execution time: 16_099_000 picoseconds. + Weight::from_parts(16_592_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -233,8 +263,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 17_785_000 picoseconds. - Weight::from_parts(18_123_000, 0) + // Minimum execution time: 17_972_000 picoseconds. + Weight::from_parts(18_379_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -254,8 +284,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `6082` - // Minimum execution time: 23_903_000 picoseconds. - Weight::from_parts(24_769_000, 0) + // Minimum execution time: 23_554_000 picoseconds. + Weight::from_parts(24_446_000, 0) .saturating_add(Weight::from_parts(0, 6082)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) @@ -266,8 +296,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 10_617_000 picoseconds. - Weight::from_parts(10_843_000, 0) + // Minimum execution time: 10_541_000 picoseconds. + Weight::from_parts(10_894_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -277,8 +307,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 16_656_000 picoseconds. - Weight::from_parts(17_106_000, 0) + // Minimum execution time: 16_404_000 picoseconds. + Weight::from_parts(16_818_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -299,8 +329,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `13507` - // Minimum execution time: 31_721_000 picoseconds. - Weight::from_parts(32_547_000, 0) + // Minimum execution time: 31_617_000 picoseconds. + Weight::from_parts(32_336_000, 0) .saturating_add(Weight::from_parts(0, 13507)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) @@ -313,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_439_000 picoseconds. - Weight::from_parts(3_619_000, 0) + // Minimum execution time: 3_328_000 picoseconds. + Weight::from_parts(3_501_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -325,8 +355,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 24_657_000 picoseconds. - Weight::from_parts(24_971_000, 0) + // Minimum execution time: 23_571_000 picoseconds. + Weight::from_parts(24_312_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -337,8 +367,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 34_028_000 picoseconds. - Weight::from_parts(34_697_000, 0) + // Minimum execution time: 32_879_000 picoseconds. + Weight::from_parts(33_385_000, 0) .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index cee17cdc7b05..ca1a915ba740 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -212,6 +212,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl parachain_info::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index cd5f1ad32728..7c9427a24939 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -282,6 +282,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; type WeightInfo = weights::pallet_message_queue::WeightInfo; } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs index fabce29b5fd9..ac494fdc719f 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -62,8 +62,28 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 17_830_000 picoseconds. - Weight::from_parts(18_411_000, 0) + // Minimum execution time: 17_935_000 picoseconds. + Weight::from_parts(18_482_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 18_311_000 picoseconds. + Weight::from_parts(18_850_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,8 +104,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 55_456_000 picoseconds. - Weight::from_parts(56_808_000, 0) + // Minimum execution time: 56_182_000 picoseconds. + Weight::from_parts(58_136_000, 0) .saturating_add(Weight::from_parts(0, 3535)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -120,14 +140,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_996_000 picoseconds. - Weight::from_parts(6_154_000, 0) + // Minimum execution time: 5_979_000 picoseconds. + Weight::from_parts(6_289_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -137,8 +167,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_768_000 picoseconds. - Weight::from_parts(1_914_000, 0) + // Minimum execution time: 1_853_000 picoseconds. + Weight::from_parts(2_045_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -162,8 +192,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 24_120_000 picoseconds. - Weight::from_parts(24_745_000, 0) + // Minimum execution time: 23_827_000 picoseconds. + Weight::from_parts(24_493_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -186,8 +216,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 26_630_000 picoseconds. - Weight::from_parts(27_289_000, 0) + // Minimum execution time: 26_755_000 picoseconds. + Weight::from_parts(27_125_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -198,8 +228,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_821_000 picoseconds. - Weight::from_parts(1_946_000, 0) + // Minimum execution time: 1_898_000 picoseconds. + Weight::from_parts(2_028_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -209,8 +239,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 16_586_000 picoseconds. - Weight::from_parts(16_977_000, 0) + // Minimum execution time: 16_300_000 picoseconds. + Weight::from_parts(16_995_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -221,8 +251,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 16_923_000 picoseconds. - Weight::from_parts(17_415_000, 0) + // Minimum execution time: 16_495_000 picoseconds. + Weight::from_parts(16_950_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -233,8 +263,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 18_596_000 picoseconds. - Weight::from_parts(18_823_000, 0) + // Minimum execution time: 18_153_000 picoseconds. + Weight::from_parts(18_595_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -254,8 +284,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 23_817_000 picoseconds. - Weight::from_parts(24_520_000, 0) + // Minimum execution time: 23_387_000 picoseconds. + Weight::from_parts(24_677_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) @@ -266,8 +296,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 11_042_000 picoseconds. - Weight::from_parts(11_578_000, 0) + // Minimum execution time: 10_939_000 picoseconds. + Weight::from_parts(11_210_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -277,8 +307,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 17_306_000 picoseconds. - Weight::from_parts(17_817_000, 0) + // Minimum execution time: 16_850_000 picoseconds. + Weight::from_parts(17_195_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -299,8 +329,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `13471` - // Minimum execution time: 32_141_000 picoseconds. - Weight::from_parts(32_954_000, 0) + // Minimum execution time: 31_931_000 picoseconds. + Weight::from_parts(32_494_000, 0) .saturating_add(Weight::from_parts(0, 13471)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) @@ -313,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_410_000 picoseconds. - Weight::from_parts(3_556_000, 0) + // Minimum execution time: 3_514_000 picoseconds. + Weight::from_parts(3_709_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -325,8 +355,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 25_021_000 picoseconds. - Weight::from_parts(25_240_000, 0) + // Minimum execution time: 24_863_000 picoseconds. + Weight::from_parts(25_293_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -337,8 +367,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 33_801_000 picoseconds. - Weight::from_parts(34_655_000, 0) + // Minimum execution time: 33_799_000 picoseconds. + Weight::from_parts(34_665_000, 0) .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index e840a40f5acd..3e331e5e8ebf 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -282,6 +282,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; type WeightInfo = weights::pallet_message_queue::WeightInfo; } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs index c337289243b7..62a9c802808c 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -62,8 +62,28 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 17_856_000 picoseconds. - Weight::from_parts(18_473_000, 0) + // Minimum execution time: 17_450_000 picoseconds. + Weight::from_parts(17_913_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 18_082_000 picoseconds. + Weight::from_parts(18_293_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,8 +104,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 56_112_000 picoseconds. - Weight::from_parts(57_287_000, 0) + // Minimum execution time: 54_939_000 picoseconds. + Weight::from_parts(55_721_000, 0) .saturating_add(Weight::from_parts(0, 3535)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -120,14 +140,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_186_000 picoseconds. - Weight::from_parts(6_420_000, 0) + // Minimum execution time: 5_789_000 picoseconds. + Weight::from_parts(5_995_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -137,8 +167,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(1_999_000, 0) + // Minimum execution time: 1_795_000 picoseconds. + Weight::from_parts(1_924_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -162,8 +192,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 23_833_000 picoseconds. - Weight::from_parts(24_636_000, 0) + // Minimum execution time: 23_445_000 picoseconds. + Weight::from_parts(23_906_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -186,8 +216,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 26_557_000 picoseconds. - Weight::from_parts(27_275_000, 0) + // Minimum execution time: 26_590_000 picoseconds. + Weight::from_parts(27_056_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -198,8 +228,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_921_000 picoseconds. - Weight::from_parts(2_040_000, 0) + // Minimum execution time: 1_889_000 picoseconds. + Weight::from_parts(1_962_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -209,8 +239,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 16_832_000 picoseconds. - Weight::from_parts(17_312_000, 0) + // Minimum execution time: 16_408_000 picoseconds. + Weight::from_parts(16_877_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -221,8 +251,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 16_687_000 picoseconds. - Weight::from_parts(17_123_000, 0) + // Minimum execution time: 16_791_000 picoseconds. + Weight::from_parts(17_111_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -233,8 +263,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 18_164_000 picoseconds. - Weight::from_parts(18_580_000, 0) + // Minimum execution time: 18_355_000 picoseconds. + Weight::from_parts(19_110_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -254,8 +284,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 23_577_000 picoseconds. - Weight::from_parts(24_324_000, 0) + // Minimum execution time: 23_354_000 picoseconds. + Weight::from_parts(23_999_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) @@ -266,8 +296,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 11_014_000 picoseconds. - Weight::from_parts(11_223_000, 0) + // Minimum execution time: 11_065_000 picoseconds. + Weight::from_parts(11_302_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -277,8 +307,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 16_887_000 picoseconds. - Weight::from_parts(17_361_000, 0) + // Minimum execution time: 16_998_000 picoseconds. + Weight::from_parts(17_509_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -299,8 +329,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `13471` - // Minimum execution time: 31_705_000 picoseconds. - Weight::from_parts(32_166_000, 0) + // Minimum execution time: 31_068_000 picoseconds. + Weight::from_parts(31_978_000, 0) .saturating_add(Weight::from_parts(0, 13471)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) @@ -313,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_568_000 picoseconds. - Weight::from_parts(3_669_000, 0) + // Minimum execution time: 3_478_000 picoseconds. + Weight::from_parts(3_595_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -325,8 +355,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 24_823_000 picoseconds. - Weight::from_parts(25_344_000, 0) + // Minimum execution time: 24_962_000 picoseconds. + Weight::from_parts(25_404_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -337,8 +367,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 34_516_000 picoseconds. - Weight::from_parts(35_478_000, 0) + // Minimum execution time: 32_685_000 picoseconds. + Weight::from_parts(33_592_000, 0) .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs index 0f4957fd802b..ad79d6849bd5 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/lib.rs @@ -232,6 +232,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 1d404feac3db..0a55d2dcfe53 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -540,6 +540,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index c60061419817..034d16267d45 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -320,6 +320,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); } impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/polkadot/node/collation-generation/src/error.rs b/polkadot/node/collation-generation/src/error.rs index ac5db6cd7f28..852c50f30682 100644 --- a/polkadot/node/collation-generation/src/error.rs +++ b/polkadot/node/collation-generation/src/error.rs @@ -28,6 +28,8 @@ pub enum Error { Util(#[from] polkadot_node_subsystem_util::Error), #[error(transparent)] Erasure(#[from] polkadot_erasure_coding::Error), + #[error("Parachain backing state not available in runtime.")] + MissingParaBackingState, } pub type Result = std::result::Result; diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index 3b1a8f5ff230..3164f6078bc0 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -44,8 +44,8 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_util::{ has_required_runtime, request_async_backing_params, request_availability_cores, - request_claim_queue, request_persisted_validation_data, request_validation_code, - request_validation_code_hash, request_validators, + request_claim_queue, request_para_backing_state, request_persisted_validation_data, + request_validation_code, request_validation_code_hash, request_validators, }; use polkadot_primitives::{ collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, @@ -212,6 +212,7 @@ async fn handle_new_activations( if config.collator.is_none() { return Ok(()) } + let para_id = config.para_id; let _overall_timer = metrics.time_new_activations(); @@ -225,25 +226,23 @@ async fn handle_new_activations( ); let availability_cores = availability_cores??; - let n_validators = validators??.len(); let async_backing_params = async_backing_params?.ok(); + let n_validators = validators??.len(); let maybe_claim_queue = fetch_claim_queue(ctx.sender(), relay_parent).await?; - for (core_idx, core) in availability_cores.into_iter().enumerate() { - let _availability_core_timer = metrics.time_new_activations_availability_core(); + // The loop bellow will fill in cores that the para is allowed to build on. + let mut cores_to_build_on = Vec::new(); - let (scheduled_core, assumption) = match core { - CoreState::Scheduled(scheduled_core) => - (scheduled_core, OccupiedCoreAssumption::Free), + for (core_idx, core) in availability_cores.into_iter().enumerate() { + let scheduled_core = match core { + CoreState::Scheduled(scheduled_core) => scheduled_core, CoreState::Occupied(occupied_core) => match async_backing_params { Some(params) if params.max_candidate_depth >= 1 => { // maximum candidate depth when building on top of a block // pending availability is necessarily 1 - the depth of the // pending block is 0 so the child has depth 1. - // TODO [now]: this assumes that next up == current. - // in practice we should only set `OccupiedCoreAssumption::Included` - // when the candidate occupying the core is also of the same para. + // Use claim queue if available, or fallback to `next_up_on_available` let res = match maybe_claim_queue { Some(ref claim_queue) => { // read what's in the claim queue for this core @@ -257,8 +256,7 @@ async fn handle_new_activations( // `next_up_on_available` occupied_core.next_up_on_available }, - } - .map(|scheduled| (scheduled, OccupiedCoreAssumption::Included)); + }; match res { Some(res) => res, @@ -279,7 +277,7 @@ async fn handle_new_activations( gum::trace!( target: LOG_TARGET, core_idx = %core_idx, - "core is free. Keep going.", + "core is not assigned to any para. Keep going.", ); continue }, @@ -294,64 +292,90 @@ async fn handle_new_activations( their_para = %scheduled_core.para_id, "core is not assigned to our para. Keep going.", ); - continue + } else { + // Accumulate cores for building collation(s) outside the loop. + cores_to_build_on.push(CoreIndex(core_idx as u32)); } + } - // we get validation data and validation code synchronously for each core instead of - // within the subtask loop, because we have only a single mutable handle to the - // context, so the work can't really be distributed - - let validation_data = match request_persisted_validation_data( - relay_parent, - scheduled_core.para_id, - assumption, - ctx.sender(), - ) - .await - .await?? - { - Some(v) => v, - None => { - gum::trace!( - target: LOG_TARGET, - core_idx = %core_idx, - relay_parent = ?relay_parent, - our_para = %config.para_id, - their_para = %scheduled_core.para_id, - "validation data is not available", - ); - continue - }, - }; + // Skip to next relay parent if there is no core assigned to us. + if cores_to_build_on.is_empty() { + continue + } - let validation_code_hash = match obtain_validation_code_hash_with_assumption( - relay_parent, - scheduled_core.para_id, - assumption, - ctx.sender(), - ) - .await? - { - Some(v) => v, - None => { - gum::trace!( - target: LOG_TARGET, - core_idx = %core_idx, - relay_parent = ?relay_parent, - our_para = %config.para_id, - their_para = %scheduled_core.para_id, - "validation code hash is not found.", - ); - continue - }, - }; + let para_backing_state = + request_para_backing_state(relay_parent, config.para_id, ctx.sender()) + .await + .await?? + .ok_or(crate::error::Error::MissingParaBackingState)?; + + // We are being very optimistic here, but one of the cores could pend availability some more + // block, ore even time out. + // For timeout assumption the collator can't really know because it doesn't receive bitfield + // gossip. + let assumption = if para_backing_state.pending_availability.is_empty() { + OccupiedCoreAssumption::Free + } else { + OccupiedCoreAssumption::Included + }; + + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %config.para_id, + ?assumption, + "Occupied core(s) assumption", + ); + + let mut validation_data = match request_persisted_validation_data( + relay_parent, + config.para_id, + assumption, + ctx.sender(), + ) + .await + .await?? + { + Some(v) => v, + None => { + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %config.para_id, + "validation data is not available", + ); + continue + }, + }; - let task_config = config.clone(); - let metrics = metrics.clone(); - let mut task_sender = ctx.sender().clone(); - ctx.spawn( - "collation-builder", - Box::pin(async move { + let validation_code_hash = match obtain_validation_code_hash_with_assumption( + relay_parent, + config.para_id, + assumption, + ctx.sender(), + ) + .await? + { + Some(v) => v, + None => { + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %config.para_id, + "validation code hash is not found.", + ); + continue + }, + }; + + let task_config = config.clone(); + let metrics = metrics.clone(); + let mut task_sender = ctx.sender().clone(); + + ctx.spawn( + "chained-collation-builder", + Box::pin(async move { + for core_index in cores_to_build_on { let collator_fn = match task_config.collator.as_ref() { Some(x) => x, None => return, @@ -363,21 +387,23 @@ async fn handle_new_activations( None => { gum::debug!( target: LOG_TARGET, - para_id = %scheduled_core.para_id, + ?para_id, "collator returned no collation on collate", ); return }, }; + let parent_head = collation.head_data.clone(); construct_and_distribute_receipt( PreparedCollation { collation, - para_id: scheduled_core.para_id, + para_id, relay_parent, - validation_data, + validation_data: validation_data.clone(), validation_code_hash, n_validators, + core_index, }, task_config.key.clone(), &mut task_sender, @@ -385,9 +411,13 @@ async fn handle_new_activations( &metrics, ) .await; - }), - )?; - } + + // Chain the collations. All else stays the same as we build the chained + // collation on same relay parent. + validation_data.parent_head = parent_head; + } + }), + )?; } Ok(()) @@ -408,6 +438,7 @@ async fn handle_submit_collation( parent_head, validation_code_hash, result_sender, + core_index, } = params; let validators = request_validators(relay_parent, ctx.sender()).await.await??; @@ -444,6 +475,7 @@ async fn handle_submit_collation( validation_data, validation_code_hash, n_validators, + core_index, }; construct_and_distribute_receipt( @@ -465,6 +497,7 @@ struct PreparedCollation { validation_data: PersistedValidationData, validation_code_hash: ValidationCodeHash, n_validators: usize, + core_index: CoreIndex, } /// Takes a prepared collation, along with its context, and produces a candidate receipt @@ -483,6 +516,7 @@ async fn construct_and_distribute_receipt( validation_data, validation_code_hash, n_validators, + core_index, } = collation; let persisted_validation_data_hash = validation_data.hash(); @@ -578,6 +612,7 @@ async fn construct_and_distribute_receipt( pov, parent_head_data, result_sender, + core_index, }) .await; } diff --git a/polkadot/node/collation-generation/src/tests.rs b/polkadot/node/collation-generation/src/tests.rs index 9b16980e6af4..3cb3e61a35a1 100644 --- a/polkadot/node/collation-generation/src/tests.rs +++ b/polkadot/node/collation-generation/src/tests.rs @@ -30,13 +30,16 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle}; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - AsyncBackingParams, CollatorPair, HeadData, Id as ParaId, Id, PersistedValidationData, + async_backing::{BackingState, CandidatePendingAvailability}, + AsyncBackingParams, BlockNumber, CollatorPair, HeadData, PersistedValidationData, ScheduledCore, ValidationCode, }; use rstest::rstest; use sp_keyring::sr25519::Keyring as Sr25519Keyring; use std::pin::Pin; -use test_helpers::{dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator}; +use test_helpers::{ + dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator, make_candidate, +}; type VirtualOverseer = TestSubsystemContextHandle; @@ -105,9 +108,9 @@ impl Future for TestCollator { impl Unpin for TestCollator {} -async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { - const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(2000); +const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(2000); +async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { overseer .recv() .timeout(TIMEOUT) @@ -135,6 +138,41 @@ fn scheduled_core_for>(para_id: Id) -> ScheduledCore { ScheduledCore { para_id: para_id.into(), collator: None } } +fn dummy_candidate_pending_availability( + para_id: ParaId, + candidate_relay_parent: Hash, + relay_parent_number: BlockNumber, +) -> CandidatePendingAvailability { + let (candidate, _pvd) = make_candidate( + candidate_relay_parent, + relay_parent_number, + para_id, + dummy_head_data(), + HeadData(vec![1]), + ValidationCode(vec![1, 2, 3]).hash(), + ); + let candidate_hash = candidate.hash(); + + CandidatePendingAvailability { + candidate_hash, + descriptor: candidate.descriptor, + commitments: candidate.commitments, + relay_parent_number, + max_pov_size: 5 * 1024 * 1024, + } +} + +fn dummy_backing_state(pending_availability: Vec) -> BackingState { + let constraints = helpers::dummy_constraints( + 0, + vec![0], + dummy_head_data(), + ValidationCodeHash::from(Hash::repeat_byte(42)), + ); + + BackingState { constraints, pending_availability } +} + #[rstest] #[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] #[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] @@ -176,6 +214,12 @@ fn requests_availability_per_relay_parent(#[case] runtime_version: u32) { ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { tx.send(Ok(BTreeMap::new())).unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ParaBackingState(_para_id, tx), + ))) => { + tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); + }, Some(msg) => panic!("didn't expect any other overseer requests given no availability cores; got {:?}", msg), } } @@ -273,6 +317,12 @@ fn requests_validation_data_for_scheduled_matches(#[case] runtime_version: u32) ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { tx.send(Ok(BTreeMap::new())).unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ParaBackingState(_para_id, tx), + ))) => { + tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); + }, Some(msg) => { panic!("didn't expect any other overseer requests; got {:?}", msg) }, @@ -384,6 +434,12 @@ fn sends_distribute_collation_message(#[case] runtime_version: u32) { ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { tx.send(Ok(BTreeMap::new())).unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ParaBackingState(_para_id, tx), + ))) => { + tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); + }, Some(msg @ AllMessages::CollatorProtocol(_)) => { inner_to_collator_protocol.lock().await.push(msg); }, @@ -564,6 +620,12 @@ fn fallback_when_no_validation_code_hash_api(#[case] runtime_version: u32) { let res = BTreeMap::>::new(); tx.send(Ok(res)).unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ParaBackingState(_para_id, tx), + ))) => { + tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); + }, Some(msg) => { panic!("didn't expect any other overseer requests; got {:?}", msg) }, @@ -611,6 +673,7 @@ fn submit_collation_is_no_op_before_initialization() { parent_head: vec![1, 2, 3].into(), validation_code_hash: Hash::repeat_byte(1).into(), result_sender: None, + core_index: CoreIndex(0), }), }) .await; @@ -647,6 +710,7 @@ fn submit_collation_leads_to_distribution() { parent_head: vec![1, 2, 3].into(), validation_code_hash, result_sender: None, + core_index: CoreIndex(0), }), }) .await; @@ -721,6 +785,9 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run test_harness(|mut virtual_overseer| async move { helpers::initialize_collator(&mut virtual_overseer, para_id).await; helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; + + let pending_availability = + vec![dummy_candidate_pending_availability(para_id, activated_hash, 1)]; helpers::handle_runtime_calls_on_new_head_activation( &mut virtual_overseer, activated_hash, @@ -728,14 +795,140 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run cores, runtime_version, claim_queue, + pending_availability, + ) + .await; + helpers::handle_core_processing_for_a_leaf( + &mut virtual_overseer, + activated_hash, + para_id, + // `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included` + OccupiedCoreAssumption::Included, + 1, + ) + .await; + + virtual_overseer + }); +} + +// There are variable number of cores of cores in `Occupied` state and async backing is enabled. +// On new head activation `CollationGeneration` should produce and distribute a new collation +// with proper assumption about the para candidate chain availability at next block. +#[rstest] +#[case(0)] +#[case(1)] +#[case(2)] +fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elastic_scaling( + #[case] candidates_pending_avail: u32, +) { + let activated_hash: Hash = [1; 32].into(); + let para_id = ParaId::from(5); + + let cores = (0..candidates_pending_avail) + .into_iter() + .map(|idx| { + CoreState::Occupied(polkadot_primitives::OccupiedCore { + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), + occupied_since: 0, + time_out_at: 10, + next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), + availability: Default::default(), // doesn't matter + group_responsible: polkadot_primitives::GroupIndex(idx as u32), + candidate_hash: Default::default(), + candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + }) + }) + .collect::>(); + + let pending_availability = (0..candidates_pending_avail) + .into_iter() + .map(|_idx| dummy_candidate_pending_availability(para_id, activated_hash, 0)) + .collect::>(); + + let claim_queue = cores + .iter() + .enumerate() + .map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id]))) + .collect::>(); + let total_cores = cores.len(); + + test_harness(|mut virtual_overseer| async move { + helpers::initialize_collator(&mut virtual_overseer, para_id).await; + helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; + helpers::handle_runtime_calls_on_new_head_activation( + &mut virtual_overseer, + activated_hash, + AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, + cores, + // Using latest runtime with the fancy claim queue exposed. + RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, + claim_queue, + pending_availability, ) .await; + helpers::handle_core_processing_for_a_leaf( &mut virtual_overseer, activated_hash, para_id, // `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included` OccupiedCoreAssumption::Included, + total_cores, + ) + .await; + + virtual_overseer + }); +} + +// There are variable number of cores of cores in `Free` state and async backing is enabled. +// On new head activation `CollationGeneration` should produce and distribute a new collation +// with proper assumption about the para candidate chain availability at next block. +#[rstest] +#[case(0)] +#[case(1)] +#[case(2)] +fn distribute_collation_for_free_cores_with_async_backing_enabled_and_elastic_scaling( + #[case] candidates_pending_avail: u32, +) { + let activated_hash: Hash = [1; 32].into(); + let para_id = ParaId::from(5); + + let cores = (0..candidates_pending_avail) + .into_iter() + .map(|_idx| CoreState::Scheduled(ScheduledCore { para_id, collator: None })) + .collect::>(); + + let claim_queue = cores + .iter() + .enumerate() + .map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id]))) + .collect::>(); + let total_cores = cores.len(); + + test_harness(|mut virtual_overseer| async move { + helpers::initialize_collator(&mut virtual_overseer, para_id).await; + helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; + helpers::handle_runtime_calls_on_new_head_activation( + &mut virtual_overseer, + activated_hash, + AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, + cores, + // Using latest runtime with the fancy claim queue exposed. + RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, + claim_queue, + vec![], + ) + .await; + + helpers::handle_core_processing_for_a_leaf( + &mut virtual_overseer, + activated_hash, + para_id, + // `CoreState` is `Free` => `OccupiedCoreAssumption` is `Free` + OccupiedCoreAssumption::Free, + total_cores, ) .await; @@ -777,6 +970,7 @@ fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled( cores, runtime_version, claim_queue, + vec![], ) .await; @@ -785,8 +979,38 @@ fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled( } mod helpers { + use polkadot_primitives::{ + async_backing::{Constraints, InboundHrmpLimitations}, + BlockNumber, + }; + use super::*; + // A set for dummy constraints for `ParaBackingState`` + pub(crate) fn dummy_constraints( + min_relay_parent_number: BlockNumber, + valid_watermarks: Vec, + required_parent: HeadData, + validation_code_hash: ValidationCodeHash, + ) -> Constraints { + Constraints { + min_relay_parent_number, + max_pov_size: 5 * 1024 * 1024, + max_code_size: 1_000_000, + ump_remaining: 10, + ump_remaining_bytes: 1_000, + max_ump_num_per_candidate: 10, + dmp_remaining_messages: vec![], + hrmp_inbound: InboundHrmpLimitations { valid_watermarks }, + hrmp_channels_out: vec![], + max_hrmp_num_per_candidate: 0, + required_parent, + validation_code_hash, + upgrade_restriction: None, + future_validation_code: None, + } + } + // Sends `Initialize` with a collator config pub async fn initialize_collator(virtual_overseer: &mut VirtualOverseer, para_id: ParaId) { virtual_overseer @@ -822,7 +1046,8 @@ mod helpers { async_backing_params: AsyncBackingParams, cores: Vec, runtime_version: u32, - claim_queue: BTreeMap>, + claim_queue: BTreeMap>, + pending_availability: Vec, ) { assert_matches!( overseer_recv(virtual_overseer).await, @@ -857,6 +1082,25 @@ mod helpers { } ); + // Process the `ParaBackingState` message, and return some dummy state. + let message = overseer_recv(virtual_overseer).await; + let para_id = match message { + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _, + RuntimeApiRequest::ParaBackingState(p_id, _), + )) => p_id, + _ => panic!("received unexpected message {:?}", message), + }; + + assert_matches!( + message, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::ParaBackingState(p_id, tx)) + ) if parent == activated_hash && p_id == para_id => { + tx.send(Ok(Some(dummy_backing_state(pending_availability)))).unwrap(); + } + ); + assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( @@ -889,7 +1133,14 @@ mod helpers { activated_hash: Hash, para_id: ParaId, expected_occupied_core_assumption: OccupiedCoreAssumption, + cores_assigned: usize, ) { + // Expect no messages if no cores is assigned to the para + if cores_assigned == 0 { + assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + return + } + // Some hardcoded data - if needed, extract to parameters let validation_code_hash = ValidationCodeHash::from(Hash::repeat_byte(42)); let parent_head = HeadData::from(vec![1, 2, 3]); diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index 1a62c9ee55e6..76b3d476e28f 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -1285,10 +1285,10 @@ fn cores_to_candidate_indices( // Map from core index to candidate index. for claimed_core_index in core_indices.iter_ones() { - // Candidates are sorted by core index. - if let Ok(candidate_index) = block_entry + if let Some(candidate_index) = block_entry .candidates() - .binary_search_by_key(&(claimed_core_index as u32), |(core_index, _)| core_index.0) + .iter() + .position(|(core_index, _)| core_index.0 == claimed_core_index as u32) { candidate_indices.push(candidate_index as _); } diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index b924a1b52ccf..6eeb99cb99ff 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -454,7 +454,7 @@ pub struct BlockEntry { slot: Slot, relay_vrf_story: RelayVRFStory, // The candidates included as-of this block and the index of the core they are - // leaving. Sorted ascending by core index. + // leaving. candidates: Vec<(CoreIndex, CandidateHash)>, // A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`. // The i'th bit is `true` iff the candidate has been approved in the context of this diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index a3013eab46dd..1483af565853 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -2479,6 +2479,173 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { }); } +// See https://github.com/paritytech/polkadot-sdk/issues/3826 +#[test] +fn inclusion_events_can_be_unordered_by_core_index() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + for core in 0..3 { + let _ = assignments.insert( + CoreIndex(core), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2( + AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }, + ), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + } + assignments + }, + |_| Ok(0), + )); + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { + mut virtual_overseer, + clock, + sync_oracle_handle: _sync_oracle_handle, + .. + } = test_harness; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + + let candidate_receipt0 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(0_u32); + receipt + }; + let candidate_receipt1 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(1_u32); + receipt + }; + let candidate_receipt2 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(2_u32); + receipt + }; + let candidate_index0 = 0; + let candidate_index1 = 1; + let candidate_index2 = 2; + + let validator0 = ValidatorIndex(0); + let validator1 = ValidatorIndex(1); + let validator2 = ValidatorIndex(2); + let validator3 = ValidatorIndex(3); + + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + ]; + let session_info = SessionInfo { + validator_groups: IndexedVec::>::from(vec![ + vec![validator0, validator1], + vec![validator2], + vec![validator3], + ]), + needed_approvals: 1, + zeroth_delay_tranche_width: 1, + relay_vrf_modulo_samples: 1, + n_delay_tranches: 1, + no_show_slots: 1, + ..session_info(&validators) + }; + + ChainBuilder::new() + .add_block( + block_hash, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot: Slot::from(0), + candidates: Some(vec![ + (candidate_receipt0.clone(), CoreIndex(2), GroupIndex(2)), + (candidate_receipt1.clone(), CoreIndex(1), GroupIndex(0)), + (candidate_receipt2.clone(), CoreIndex(0), GroupIndex(1)), + ]), + session_info: Some(session_info), + end_syncing: true, + }, + ) + .build(&mut virtual_overseer) + .await; + + assert_eq!(clock.inner.lock().next_wakeup().unwrap(), 2); + clock.inner.lock().wakeup_all(100); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(100)).await; + + // Assignment is distributed only once from `approval-voting` + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(c_indices, vec![candidate_index0, candidate_index1, candidate_index2].try_into().unwrap()); + } + ); + + // Candidate 0 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Candidate 1 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Candidate 2 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Check if assignment was triggered for candidate 0. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt0.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + // Check if assignment was triggered for candidate 1. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt1.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + // Check if assignment was triggered for candidate 2. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt2.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + virtual_overseer + }); +} + fn approved_ancestor_test( skip_approval: impl Fn(BlockNumber) -> bool, approved_height: BlockNumber, diff --git a/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs b/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs index 019eb1222082..c33674a8f2f9 100644 --- a/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs +++ b/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs @@ -27,6 +27,7 @@ use polkadot_subsystem_bench::{ availability::{benchmark_availability_write, prepare_test, TestState}, configuration::TestConfiguration, usage::BenchmarkUsage, + utils::save_to_file, }; use std::io::Write; @@ -60,7 +61,13 @@ fn main() -> Result<(), String> { }) .collect(); println!("\rDone!{}", " ".repeat(BENCH_COUNT)); + let average_usage = BenchmarkUsage::average(&usages); + save_to_file( + "charts/availability-distribution-regression-bench.json", + average_usage.to_chart_json().map_err(|e| e.to_string())?, + ) + .map_err(|e| e.to_string())?; println!("{}", average_usage); // We expect no variance for received and sent diff --git a/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs b/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs index 5e8b81be82dd..46a38516898f 100644 --- a/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs +++ b/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs @@ -28,6 +28,7 @@ use polkadot_subsystem_bench::{ }, configuration::TestConfiguration, usage::BenchmarkUsage, + utils::save_to_file, }; use std::io::Write; @@ -58,7 +59,13 @@ fn main() -> Result<(), String> { }) .collect(); println!("\rDone!{}", " ".repeat(BENCH_COUNT)); + let average_usage = BenchmarkUsage::average(&usages); + save_to_file( + "charts/availability-recovery-regression-bench.json", + average_usage.to_chart_json().map_err(|e| e.to_string())?, + ) + .map_err(|e| e.to_string())?; println!("{}", average_usage); // We expect no variance for received and sent diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index 9f306f288a16..e6aa55235b7a 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -203,20 +203,40 @@ struct PeerData { version: CollationVersion, } +/// A type wrapping a collation and it's designated core index. +struct CollationWithCoreIndex(Collation, CoreIndex); + +impl CollationWithCoreIndex { + /// Returns inner collation ref. + pub fn collation(&self) -> &Collation { + &self.0 + } + + /// Returns inner collation mut ref. + pub fn collation_mut(&mut self) -> &mut Collation { + &mut self.0 + } + + /// Returns inner core index. + pub fn core_index(&self) -> &CoreIndex { + &self.1 + } +} + struct PerRelayParent { prospective_parachains_mode: ProspectiveParachainsMode, - /// Validators group responsible for backing candidates built + /// Per core index validators group responsible for backing candidates built /// on top of this relay parent. - validator_group: ValidatorGroup, + validator_group: HashMap, /// Distributed collations. - collations: HashMap, + collations: HashMap, } impl PerRelayParent { fn new(mode: ProspectiveParachainsMode) -> Self { Self { prospective_parachains_mode: mode, - validator_group: ValidatorGroup::default(), + validator_group: HashMap::default(), collations: HashMap::new(), } } @@ -350,6 +370,7 @@ async fn distribute_collation( pov: PoV, parent_head_data: HeadData, result_sender: Option>, + core_index: CoreIndex, ) -> Result<()> { let candidate_relay_parent = receipt.descriptor.relay_parent; let candidate_hash = receipt.hash(); @@ -422,7 +443,22 @@ async fn distribute_collation( ); } - let our_core = our_cores[0]; + // Double check that the specified `core_index` is among the ones our para has assignments for. + if !our_cores.iter().any(|assigned_core| assigned_core == &core_index) { + gum::warn!( + target: LOG_TARGET, + para_id = %id, + relay_parent = ?candidate_relay_parent, + cores = ?our_cores, + ?core_index, + "Attempting to distribute collation for a core we are not assigned to ", + ); + + return Ok(()) + } + + let our_core = core_index; + // Determine the group on that core. // // When prospective parachains are disabled, candidate relay parent here is @@ -464,10 +500,12 @@ async fn distribute_collation( "Accepted collation, connecting to validators." ); - let validators_at_relay_parent = &mut per_relay_parent.validator_group.validators; - if validators_at_relay_parent.is_empty() { - *validators_at_relay_parent = validators; - } + // Insert validator group for the `core_index` at relay parent. + per_relay_parent.validator_group.entry(core_index).or_insert_with(|| { + let mut group = ValidatorGroup::default(); + group.validators = validators; + group + }); // Update a set of connected validators if necessary. connect_to_validators(ctx, &state.validator_groups_buf).await; @@ -484,7 +522,10 @@ async fn distribute_collation( per_relay_parent.collations.insert( candidate_hash, - Collation { receipt, pov, parent_head_data, status: CollationStatus::Created }, + CollationWithCoreIndex( + Collation { receipt, pov, parent_head_data, status: CollationStatus::Created }, + core_index, + ), ); // If prospective parachains are disabled, a leaf should be known to peer. @@ -690,7 +731,10 @@ async fn advertise_collation( advertisement_timeouts: &mut FuturesUnordered, metrics: &Metrics, ) { - for (candidate_hash, collation) in per_relay_parent.collations.iter_mut() { + for (candidate_hash, collation_and_core) in per_relay_parent.collations.iter_mut() { + let core_index = *collation_and_core.core_index(); + let collation = collation_and_core.collation_mut(); + // Check that peer will be able to request the collation. if let CollationVersion::V1 = protocol_version { if per_relay_parent.prospective_parachains_mode.is_enabled() { @@ -704,11 +748,17 @@ async fn advertise_collation( } } - let should_advertise = - per_relay_parent - .validator_group - .should_advertise_to(candidate_hash, peer_ids, &peer); + let Some(validator_group) = per_relay_parent.validator_group.get_mut(&core_index) else { + gum::debug!( + target: LOG_TARGET, + ?relay_parent, + ?core_index, + "Skipping advertising to validator, validator group for core not found", + ); + return + }; + let should_advertise = validator_group.should_advertise_to(candidate_hash, peer_ids, &peer); match should_advertise { ShouldAdvertiseTo::Yes => {}, ShouldAdvertiseTo::NotAuthority | ShouldAdvertiseTo::AlreadyAdvertised => { @@ -756,9 +806,7 @@ async fn advertise_collation( )) .await; - per_relay_parent - .validator_group - .advertised_to_peer(candidate_hash, &peer_ids, peer); + validator_group.advertised_to_peer(candidate_hash, &peer_ids, peer); advertisement_timeouts.push(ResetInterestTimeout::new( *candidate_hash, @@ -790,6 +838,7 @@ async fn process_msg( pov, parent_head_data, result_sender, + core_index, } => { let _span1 = state .span_per_relay_parent @@ -820,6 +869,7 @@ async fn process_msg( pov, parent_head_data, result_sender, + core_index, ) .await?; }, @@ -1053,7 +1103,7 @@ async fn handle_incoming_request( }; let mode = per_relay_parent.prospective_parachains_mode; - let collation = match &req { + let collation_with_core = match &req { VersionedCollationRequest::V1(_) if !mode.is_enabled() => per_relay_parent.collations.values_mut().next(), VersionedCollationRequest::V2(req) => @@ -1070,22 +1120,24 @@ async fn handle_incoming_request( return Ok(()) }, }; - let (receipt, pov, parent_head_data) = if let Some(collation) = collation { - collation.status.advance_to_requested(); - ( - collation.receipt.clone(), - collation.pov.clone(), - collation.parent_head_data.clone(), - ) - } else { - gum::warn!( - target: LOG_TARGET, - relay_parent = %relay_parent, - "received a `RequestCollation` for a relay parent we don't have collation stored.", - ); + let (receipt, pov, parent_head_data) = + if let Some(collation_with_core) = collation_with_core { + let collation = collation_with_core.collation_mut(); + collation.status.advance_to_requested(); + ( + collation.receipt.clone(), + collation.pov.clone(), + collation.parent_head_data.clone(), + ) + } else { + gum::warn!( + target: LOG_TARGET, + relay_parent = %relay_parent, + "received a `RequestCollation` for a relay parent we don't have collation stored.", + ); - return Ok(()) - }; + return Ok(()) + }; state.metrics.on_collation_sent_requested(); @@ -1340,7 +1392,9 @@ where .remove(removed) .map(|per_relay_parent| per_relay_parent.collations) .unwrap_or_default(); - for collation in collations.into_values() { + for collation_with_core in collations.into_values() { + let collation = collation_with_core.collation(); + let candidate_hash = collation.receipt.hash(); state.collation_result_senders.remove(&candidate_hash); state.validator_groups_buf.remove_candidate(&candidate_hash); @@ -1477,7 +1531,7 @@ async fn run_inner( continue }; - let next_collation = { + let next_collation_with_core = { let per_relay_parent = match state.per_relay_parent.get(&relay_parent) { Some(per_relay_parent) => per_relay_parent, None => continue, @@ -1497,7 +1551,8 @@ async fn run_inner( } }; - if let Some(collation) = next_collation { + if let Some(collation_with_core) = next_collation_with_core { + let collation = collation_with_core.collation(); let receipt = collation.receipt.clone(); let pov = collation.pov.clone(); let parent_head_data = collation.parent_head_data.clone(); diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs index 38e6780eb7d2..bcf0b34e631f 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs @@ -377,6 +377,7 @@ async fn distribute_collation_with_receipt( pov: pov.clone(), parent_head_data: HeadData(vec![1, 2, 3]), result_sender: None, + core_index: CoreIndex(0), }, ) .await; diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs index e419cd5444f5..707053545630 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs @@ -277,6 +277,7 @@ fn distribute_collation_from_implicit_view() { pov: pov.clone(), parent_head_data: HeadData(vec![1, 2, 3]), result_sender: None, + core_index: CoreIndex(0), }, ) .await; @@ -358,6 +359,7 @@ fn distribute_collation_up_to_limit() { pov: pov.clone(), parent_head_data: HeadData(vec![1, 2, 3]), result_sender: None, + core_index: CoreIndex(0), }, ) .await; diff --git a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs index 1533f2eda5a5..fbb3ff4328a5 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs @@ -45,14 +45,22 @@ use futures::FutureExt; use polkadot_node_network_protocol::PeerId; use polkadot_primitives::{AuthorityDiscoveryId, CandidateHash, GroupIndex, SessionIndex}; +/// Elastic scaling: how many candidates per relay chain block the collator supports building. +pub const MAX_CHAINED_CANDIDATES_PER_RCB: NonZeroUsize = match NonZeroUsize::new(3) { + Some(cap) => cap, + None => panic!("max candidates per rcb cannot be zero"), +}; + /// The ring buffer stores at most this many unique validator groups. /// /// This value should be chosen in way that all groups assigned to our para -/// in the view can fit into the buffer. -pub const VALIDATORS_BUFFER_CAPACITY: NonZeroUsize = match NonZeroUsize::new(3) { - Some(cap) => cap, - None => panic!("buffer capacity must be non-zero"), -}; +/// in the view can fit into the buffer multiplied by amount of candidates we support per relay +/// chain block in the case of elastic scaling. +pub const VALIDATORS_BUFFER_CAPACITY: NonZeroUsize = + match NonZeroUsize::new(3 * MAX_CHAINED_CANDIDATES_PER_RCB.get()) { + Some(cap) => cap, + None => panic!("buffer capacity must be non-zero"), + }; /// Unique identifier of a validators group. #[derive(Debug)] diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index b102cf06c38f..b127d87d4ea4 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -31,8 +31,8 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use polkadot_primitives::{ BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, CollatorPair, - CommittedCandidateReceipt, CompactStatement, EncodeAs, Hash, HashT, HeadData, Id as ParaId, - PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode, + CommittedCandidateReceipt, CompactStatement, CoreIndex, EncodeAs, Hash, HashT, HeadData, + Id as ParaId, PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorIndex, MAX_CODE_SIZE, MAX_POV_SIZE, }; pub use sp_consensus_babe::{ @@ -524,6 +524,8 @@ pub struct SubmitCollationParams { /// okay to just drop it. However, if it is called, it should be called with the signed /// statement of a parachain validator seconding the collation. pub result_sender: Option>, + /// The core index on which the resulting candidate should be backed + pub core_index: CoreIndex, } /// This is the data we keep available for each candidate included in the relay chain. diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index e2bccfa55109..5a42443c84c8 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -142,6 +142,9 @@ polkadot-node-core-pvf-checker = { path = "../core/pvf-checker", optional = true polkadot-node-core-runtime-api = { path = "../core/runtime-api", optional = true } polkadot-statement-distribution = { path = "../network/statement-distribution", optional = true } +xcm = { package = "staging-xcm", path = "../../xcm" } +xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api" } + [dev-dependencies] polkadot-test-client = { path = "../test/client" } polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index 1c44b17b6fd2..c03ce1db0943 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -123,7 +123,8 @@ fn default_parachains_host_configuration( ) -> polkadot_runtime_parachains::configuration::HostConfiguration { use polkadot_primitives::{ - vstaging::node_features::FeatureIndex, AsyncBackingParams, MAX_CODE_SIZE, MAX_POV_SIZE, + vstaging::{node_features::FeatureIndex, ApprovalVotingParams}, + AsyncBackingParams, MAX_CODE_SIZE, MAX_POV_SIZE, }; polkadot_runtime_parachains::configuration::HostConfiguration { @@ -158,7 +159,8 @@ fn default_parachains_host_configuration( allowed_ancestry_len: 2, }, node_features: bitvec::vec::BitVec::from_element( - 1u8 << (FeatureIndex::ElasticScalingMVP as usize), + 1u8 << (FeatureIndex::ElasticScalingMVP as usize) | + 1u8 << (FeatureIndex::EnableAssignmentsV2 as usize), ), scheduler_params: SchedulerParams { lookahead: 2, @@ -166,6 +168,7 @@ fn default_parachains_host_configuration( paras_availability_period: 4, ..Default::default() }, + approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 5 }, ..Default::default() } } diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 085ea93fdc78..c6cfb7a27d04 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -30,6 +30,7 @@ use polkadot_primitives::{ ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; + use sp_core::OpaqueMetadata; use sp_runtime::{ traits::Block as BlockT, @@ -39,7 +40,7 @@ use sp_runtime::{ use sp_version::RuntimeVersion; use sp_weights::Weight; use std::collections::BTreeMap; - +use xcm::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; sp_api::decl_runtime_apis! { /// This runtime API is only implemented for the test runtime! pub trait GetLastTimestamp { @@ -396,4 +397,22 @@ sp_api::impl_runtime_apis! { unimplemented!() } } + + impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(_: xcm::Version) -> Result, xcm_fee_payment_runtime_api::Error> { + unimplemented!() + } + + fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result { + unimplemented!() + } + + fn query_xcm_weight(_: VersionedXcm<()>) -> Result { + unimplemented!() + } + + fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { + unimplemented!() + } + } } diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 05907e428f92..b494f05180d1 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -71,6 +71,7 @@ prometheus_endpoint = { package = "substrate-prometheus-endpoint", path = "../.. prometheus = { version = "0.13.0", default-features = false } serde = { workspace = true, default-features = true } serde_yaml = { workspace = true } +serde_json = { workspace = true } polkadot-node-core-approval-voting = { path = "../core/approval-voting" } polkadot-approval-distribution = { path = "../network/approval-distribution" } diff --git a/polkadot/node/subsystem-bench/src/lib/environment.rs b/polkadot/node/subsystem-bench/src/lib/environment.rs index 2d80d75a14aa..42955d030223 100644 --- a/polkadot/node/subsystem-bench/src/lib/environment.rs +++ b/polkadot/node/subsystem-bench/src/lib/environment.rs @@ -404,7 +404,7 @@ impl TestEnvironment { let total_cpu = test_env_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum"); usage.push(ResourceUsage { - resource_name: "Test environment".to_string(), + resource_name: "test-environment".to_string(), total: total_cpu, per_block: total_cpu / num_blocks, }); diff --git a/polkadot/node/subsystem-bench/src/lib/lib.rs b/polkadot/node/subsystem-bench/src/lib/lib.rs index d06f2822a895..ef2724abc989 100644 --- a/polkadot/node/subsystem-bench/src/lib/lib.rs +++ b/polkadot/node/subsystem-bench/src/lib/lib.rs @@ -26,3 +26,4 @@ pub(crate) mod keyring; pub(crate) mod mock; pub(crate) mod network; pub mod usage; +pub mod utils; diff --git a/polkadot/node/subsystem-bench/src/lib/usage.rs b/polkadot/node/subsystem-bench/src/lib/usage.rs index 7172969a8f92..59296746ec3d 100644 --- a/polkadot/node/subsystem-bench/src/lib/usage.rs +++ b/polkadot/node/subsystem-bench/src/lib/usage.rs @@ -82,6 +82,27 @@ impl BenchmarkUsage { _ => None, } } + + // Prepares a json string for a graph representation + // See: https://github.com/benchmark-action/github-action-benchmark?tab=readme-ov-file#examples + pub fn to_chart_json(&self) -> color_eyre::eyre::Result { + let chart = self + .network_usage + .iter() + .map(|v| ChartItem { + name: v.resource_name.clone(), + unit: "KiB".to_string(), + value: v.per_block, + }) + .chain(self.cpu_usage.iter().map(|v| ChartItem { + name: v.resource_name.clone(), + unit: "seconds".to_string(), + value: v.per_block, + })) + .collect::>(); + + Ok(serde_json::to_string(&chart)?) + } } fn check_usage( @@ -151,3 +172,10 @@ impl ResourceUsage { } type ResourceUsageCheck<'a> = (&'a str, f64, f64); + +#[derive(Debug, Serialize)] +pub struct ChartItem { + pub name: String, + pub unit: String, + pub value: f64, +} diff --git a/polkadot/node/subsystem-bench/src/lib/utils.rs b/polkadot/node/subsystem-bench/src/lib/utils.rs index cd206d8f3223..b3cd3a88b6c1 100644 --- a/polkadot/node/subsystem-bench/src/lib/utils.rs +++ b/polkadot/node/subsystem-bench/src/lib/utils.rs @@ -16,61 +16,26 @@ //! Test utils -use crate::usage::BenchmarkUsage; -use std::io::{stdout, Write}; - -pub struct WarmUpOptions<'a> { - /// The maximum number of runs considered for warming up. - pub warm_up: usize, - /// The number of runs considered for benchmarking. - pub bench: usize, - /// The difference in CPU usage between runs considered as normal - pub precision: f64, - /// The subsystems whose CPU usage is checked during warm-up cycles - pub subsystems: &'a [&'a str], -} - -impl<'a> WarmUpOptions<'a> { - pub fn new(subsystems: &'a [&'a str]) -> Self { - Self { warm_up: 100, bench: 3, precision: 0.02, subsystems } - } -} - -pub fn warm_up_and_benchmark( - options: WarmUpOptions, - run: impl Fn() -> BenchmarkUsage, -) -> Result { - println!("Warming up..."); - let mut usages = Vec::with_capacity(options.bench); - - for n in 1..=options.warm_up { - let curr = run(); - if let Some(prev) = usages.last() { - let diffs = options - .subsystems - .iter() - .map(|&v| { - curr.cpu_usage_diff(prev, v) - .ok_or(format!("{} not found in benchmark {:?}", v, prev)) - }) - .collect::, String>>()?; - if !diffs.iter().all(|&v| v < options.precision) { - usages.clear(); - } - } - usages.push(curr); - print!("\r{}%", n * 100 / options.warm_up); - if usages.len() == options.bench { - println!("\rTook {} runs to warm up", n.saturating_sub(options.bench)); - break; - } - stdout().flush().unwrap(); - } - - if usages.len() != options.bench { - println!("Didn't warm up after {} runs", options.warm_up); - return Err("Can't warm up".to_string()) +use std::{fs::File, io::Write}; + +// Saves a given string to a file +pub fn save_to_file(path: &str, value: String) -> color_eyre::eyre::Result<()> { + let output = std::process::Command::new(env!("CARGO")) + .arg("locate-project") + .arg("--workspace") + .arg("--message-format=plain") + .output() + .unwrap() + .stdout; + let workspace_dir = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()) + .parent() + .unwrap(); + let path = workspace_dir.join(path); + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; } + let mut file = File::create(path)?; + file.write_all(value.as_bytes())?; - Ok(BenchmarkUsage::average(&usages)) + Ok(()) } diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 5d05d2b56ed0..d84b0b6dd141 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -228,6 +228,8 @@ pub enum CollatorProtocolMessage { /// The result sender should be informed when at least one parachain validator seconded the /// collation. It is also completely okay to just drop the sender. result_sender: Option>, + /// The core index where the candidate should be backed. + core_index: CoreIndex, }, /// Report a collator as having provided an invalid collation. This should lead to disconnect /// and blacklist of the collator. diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index 6ff09ed5f220..83b046f0bf0a 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -30,7 +30,7 @@ use polkadot_node_subsystem::{ messages::{RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender}, overseer, SubsystemSender, }; -use polkadot_primitives::{slashing, CoreIndex, ExecutorParams}; +use polkadot_primitives::{async_backing::BackingState, slashing, CoreIndex, ExecutorParams}; pub use overseer::{ gen::{OrchestraError as OverseerError, Timeout}, @@ -308,6 +308,7 @@ specialize_requests! { fn request_disabled_validators() -> Vec; DisabledValidators; fn request_async_backing_params() -> AsyncBackingParams; AsyncBackingParams; fn request_claim_queue() -> BTreeMap>; ClaimQueue; + fn request_para_backing_state(para_id: ParaId) -> Option; ParaBackingState; } /// Requests executor parameters from the runtime effective at given relay-parent. First obtains diff --git a/polkadot/parachain/test-parachains/adder/collator/Cargo.toml b/polkadot/parachain/test-parachains/adder/collator/Cargo.toml index cb283c271191..30bce806f9ff 100644 --- a/polkadot/parachain/test-parachains/adder/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/adder/collator/Cargo.toml @@ -24,7 +24,7 @@ log = { workspace = true, default-features = true } test-parachain-adder = { path = ".." } polkadot-primitives = { path = "../../../../primitives" } polkadot-cli = { path = "../../../../cli" } -polkadot-service = { path = "../../../../node/service", features = ["rococo-native"] } +polkadot-service = { path = "../../../../node/service", features = ["elastic-scaling-experimental", "rococo-native"] } polkadot-node-primitives = { path = "../../../../node/primitives" } polkadot-node-subsystem = { path = "../../../../node/subsystem" } diff --git a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml index 238b98a66801..bede10a7673b 100644 --- a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml @@ -24,7 +24,7 @@ log = { workspace = true, default-features = true } test-parachain-undying = { path = ".." } polkadot-primitives = { path = "../../../../primitives" } polkadot-cli = { path = "../../../../cli" } -polkadot-service = { path = "../../../../node/service", features = ["rococo-native"] } +polkadot-service = { path = "../../../../node/service", features = ["elastic-scaling-experimental", "rococo-native"] } polkadot-node-primitives = { path = "../../../../node/primitives" } polkadot-node-subsystem = { path = "../../../../node/subsystem" } diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index eb9646d7e869..9095cd90ae0c 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -83,6 +83,8 @@ enum CoretimeCalls { SetLease(pallet_broker::TaskId, pallet_broker::Timeslice), #[codec(index = 19)] NotifyCoreCount(u16), + #[codec(index = 99)] + SwapLeases(ParaId, ParaId), } #[frame_support::pallet] @@ -233,6 +235,24 @@ impl Pallet { } } } + + // Handle legacy swaps in coretime. Notifies broker parachain that a lease swap has occurred via + // XCM message. This function is meant to be used in an implementation of `OnSwap` trait. + pub fn on_legacy_lease_swap(one: ParaId, other: ParaId) { + let message = Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + mk_coretime_call(crate::coretime::CoretimeCalls::SwapLeases(one, other)), + ]); + if let Err(err) = send_xcm::( + Location::new(0, [Junction::Parachain(T::BrokerId::get())]), + message, + ) { + log::error!("Sending `SwapLeases` to coretime chain failed: {:?}", err); + } + } } impl OnNewSession> for Pallet { diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 7ed62a392e4e..461b9f4b431a 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -365,6 +365,7 @@ impl pallet_message_queue::Config for Test { type HeapSize = ConstU32<65536>; type MaxStale = ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); } parameter_types! { diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 3dc59cc17281..6f63a93cebe5 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -104,6 +104,7 @@ polkadot-parachain-primitives = { path = "../../parachain", default-features = f xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api", default-features = false } [dev-dependencies] tiny-keccak = { version = "2.0.2", features = ["keccak"] } @@ -208,6 +209,7 @@ std = [ "tx-pool-api/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] runtime-benchmarks = [ diff --git a/polkadot/runtime/rococo/src/impls.rs b/polkadot/runtime/rococo/src/impls.rs index ac7100d78583..cf364b6ac794 100644 --- a/polkadot/runtime/rococo/src/impls.rs +++ b/polkadot/runtime/rococo/src/impls.rs @@ -167,11 +167,16 @@ where }, ]); + let encoded_versioned_xcm = + VersionedXcm::V4(program).encode().try_into().map_err(|error| { + log::error!(target: "runtime::on_reap_identity", "XCM too large, error: {:?}", error); + pallet_xcm::Error::::XcmTooLarge + })?; // send - let _ = >::send( + let _ = >::send_blob( RawOrigin::Root.into(), Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedXcm::V4(program)), + encoded_versioned_xcm, )?; Ok(()) } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 90824a2f6f08..c41ffdbe72db 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -38,7 +38,7 @@ use runtime_common::{ LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedLocationConverter, }, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, - traits::Leaser, + traits::{Leaser, OnSwap}, BlockHashCount, BlockLength, SlowAdjustingFeeUpdate, }; use scale_info::TypeInfo; @@ -75,7 +75,7 @@ use frame_support::{ InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter}, + weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -98,7 +98,10 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{latest::prelude::*, VersionedLocation}; +use xcm::{ + latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; @@ -123,6 +126,7 @@ use governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer, TreasurySpender, }; +use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; #[cfg(test)] mod tests; @@ -216,7 +220,7 @@ pub struct OriginPrivilegeCmp; impl PrivilegeCmp for OriginPrivilegeCmp { fn cmp_privilege(left: &OriginCaller, right: &OriginCaller) -> Option { if left == right { - return Some(Ordering::Equal) + return Some(Ordering::Equal); } match (left, right) { @@ -983,6 +987,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = MessageQueueHeapSize; type MaxStale = MessageQueueMaxStale; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; #[cfg(not(feature = "runtime-benchmarks"))] type MessageProcessor = MessageProcessor; #[cfg(feature = "runtime-benchmarks")] @@ -1078,7 +1083,7 @@ impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type OnSwap = (Crowdloan, Slots); + type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; @@ -1306,6 +1311,14 @@ impl pallet_asset_rate::Config for Runtime { type BenchmarkHelper = runtime_common::impls::benchmarks::AssetRateArguments; } +// Notify `coretime` pallet when a lease swap occurs +pub struct SwapLeases; +impl OnSwap for SwapLeases { + fn on_swap(one: ParaId, other: ParaId) { + coretime::Pallet::::on_legacy_lease_swap(one, other); + } +} + construct_runtime! { pub enum Runtime { @@ -1485,11 +1498,11 @@ pub mod migrations { let now = frame_system::Pallet::::block_number(); let lease = slots::Pallet::::lease(para); if lease.is_empty() { - return None + return None; } // Lease not yet started, ignore: if lease.iter().any(Option::is_none) { - return None + return None; } let (index, _) = as Leaser>::lease_period_index(now)?; @@ -1551,7 +1564,7 @@ pub mod migrations { fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)"); - return Ok(Vec::new()) + return Ok(Vec::new()); } log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state"); @@ -1580,7 +1593,7 @@ pub mod migrations { fn on_runtime_upgrade() -> Weight { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::info!("Skipping session keys upgrade: already applied"); - return ::DbWeight::get().reads(1) + return ::DbWeight::get().reads(1); } log::trace!("Upgrading session keys"); Session::upgrade_keys::(transform_session_keys); @@ -1593,7 +1606,7 @@ pub mod migrations { ) -> Result<(), sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)"); - return Ok(()) + return Ok(()); } let key_ids = SessionKeys::key_ids(); @@ -1777,6 +1790,37 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + Ok([VersionedAssetId::V4(xcm_config::TokenLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::TokenLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + impl sp_api::Metadata for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) @@ -2485,7 +2529,7 @@ mod remote_tests { #[tokio::test] async fn run_migrations() { if var("RUN_MIGRATION_TESTS").is_err() { - return + return; } sp_tracing::try_init_simple(); diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs index 5544ca44658c..42972baa1c83 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -60,8 +60,26 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 25_043_000 picoseconds. - Weight::from_parts(25_682_000, 0) + // Minimum execution time: 24_724_000 picoseconds. + Weight::from_parts(25_615_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 24_709_000 picoseconds. + Weight::from_parts(25_326_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -80,8 +98,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 107_570_000 picoseconds. - Weight::from_parts(109_878_000, 0) + // Minimum execution time: 106_600_000 picoseconds. + Weight::from_parts(110_781_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) @@ -100,8 +118,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `232` // Estimated: `3697` - // Minimum execution time: 106_341_000 picoseconds. - Weight::from_parts(109_135_000, 0) + // Minimum execution time: 103_030_000 picoseconds. + Weight::from_parts(106_018_000, 0) .saturating_add(Weight::from_parts(0, 3697)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) @@ -120,8 +138,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 108_372_000 picoseconds. - Weight::from_parts(112_890_000, 0) + // Minimum execution time: 107_017_000 picoseconds. + Weight::from_parts(109_214_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) @@ -130,8 +148,16 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_957_000 picoseconds. - Weight::from_parts(7_417_000, 0) + // Minimum execution time: 6_864_000 picoseconds. + Weight::from_parts(7_135_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_955_000 picoseconds. + Weight::from_parts(7_165_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::SupportedVersion` (r:0 w:1) @@ -140,8 +166,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_053_000 picoseconds. - Weight::from_parts(7_462_000, 0) + // Minimum execution time: 6_827_000 picoseconds. + Weight::from_parts(7_211_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -149,8 +175,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_918_000 picoseconds. - Weight::from_parts(2_037_000, 0) + // Minimum execution time: 1_788_000 picoseconds. + Weight::from_parts(2_021_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -171,8 +197,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 30_417_000 picoseconds. - Weight::from_parts(31_191_000, 0) + // Minimum execution time: 30_627_000 picoseconds. + Weight::from_parts(31_350_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) @@ -193,8 +219,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `360` // Estimated: `3825` - // Minimum execution time: 36_666_000 picoseconds. - Weight::from_parts(37_779_000, 0) + // Minimum execution time: 36_688_000 picoseconds. + Weight::from_parts(37_345_000, 0) .saturating_add(Weight::from_parts(0, 3825)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -205,8 +231,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_869_000 picoseconds. - Weight::from_parts(2_003_000, 0) + // Minimum execution time: 1_829_000 picoseconds. + Weight::from_parts(1_986_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -216,8 +242,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `22` // Estimated: `13387` - // Minimum execution time: 16_188_000 picoseconds. - Weight::from_parts(16_435_000, 0) + // Minimum execution time: 16_104_000 picoseconds. + Weight::from_parts(16_464_000, 0) .saturating_add(Weight::from_parts(0, 13387)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -228,8 +254,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `26` // Estimated: `13391` - // Minimum execution time: 16_431_000 picoseconds. - Weight::from_parts(16_935_000, 0) + // Minimum execution time: 16_267_000 picoseconds. + Weight::from_parts(16_675_000, 0) .saturating_add(Weight::from_parts(0, 13391)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -240,8 +266,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `40` // Estimated: `15880` - // Minimum execution time: 18_460_000 picoseconds. - Weight::from_parts(18_885_000, 0) + // Minimum execution time: 18_487_000 picoseconds. + Weight::from_parts(19_102_000, 0) .saturating_add(Weight::from_parts(0, 15880)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -259,8 +285,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `216` // Estimated: `6156` - // Minimum execution time: 29_623_000 picoseconds. - Weight::from_parts(30_661_000, 0) + // Minimum execution time: 29_603_000 picoseconds. + Weight::from_parts(31_002_000, 0) .saturating_add(Weight::from_parts(0, 6156)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -271,8 +297,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `69` // Estimated: `10959` - // Minimum execution time: 12_043_000 picoseconds. - Weight::from_parts(12_360_000, 0) + // Minimum execution time: 12_183_000 picoseconds. + Weight::from_parts(12_587_000, 0) .saturating_add(Weight::from_parts(0, 10959)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -282,8 +308,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `33` // Estimated: `13398` - // Minimum execution time: 16_511_000 picoseconds. - Weight::from_parts(17_011_000, 0) + // Minimum execution time: 16_372_000 picoseconds. + Weight::from_parts(16_967_000, 0) .saturating_add(Weight::from_parts(0, 13398)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -302,8 +328,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `216` // Estimated: `13581` - // Minimum execution time: 39_041_000 picoseconds. - Weight::from_parts(39_883_000, 0) + // Minimum execution time: 38_904_000 picoseconds. + Weight::from_parts(39_983_000, 0) .saturating_add(Weight::from_parts(0, 13581)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) @@ -316,8 +342,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_030_000 picoseconds. - Weight::from_parts(2_150_000, 0) + // Minimum execution time: 2_067_000 picoseconds. + Weight::from_parts(2_195_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -328,8 +354,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 22_615_000 picoseconds. - Weight::from_parts(23_008_000, 0) + // Minimum execution time: 23_982_000 picoseconds. + Weight::from_parts(24_409_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -340,8 +366,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `23` // Estimated: `3488` - // Minimum execution time: 34_438_000 picoseconds. - Weight::from_parts(35_514_000, 0) + // Minimum execution time: 33_430_000 picoseconds. + Weight::from_parts(34_433_000, 0) .saturating_add(Weight::from_parts(0, 3488)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8a3cd9309dbd..62c3741c56d6 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -27,10 +27,11 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use polkadot_runtime_parachains::{ assigner_parachains as parachains_assigner_parachains, configuration as parachains_configuration, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, - origin as parachains_origin, paras as parachains_paras, - paras_inherent as parachains_paras_inherent, runtime_api_impl::v7 as runtime_impl, + disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, + paras_inherent as parachains_paras_inherent, + runtime_api_impl::{v7 as runtime_impl, vstaging as staging_runtime_impl}, scheduler as parachains_scheduler, session_info as parachains_session_info, shared as parachains_shared, }; @@ -829,6 +830,7 @@ sp_api::impl_runtime_apis! { } } + #[api_version(10)] impl primitives::runtime_api::ParachainHost for Runtime { fn validators() -> Vec { runtime_impl::validators::() @@ -956,6 +958,30 @@ sp_api::impl_runtime_apis! { key_ownership_proof, ) } + + fn minimum_backing_votes() -> u32 { + runtime_impl::minimum_backing_votes::() + } + + fn para_backing_state(para_id: ParaId) -> Option { + runtime_impl::backing_state::(para_id) + } + + fn async_backing_params() -> primitives::AsyncBackingParams { + runtime_impl::async_backing_params::() + } + + fn approval_voting_params() -> primitives::vstaging::ApprovalVotingParams { + staging_runtime_impl::approval_voting_params::() + } + + fn disabled_validators() -> Vec { + staging_runtime_impl::disabled_validators::() + } + + fn node_features() -> primitives::vstaging::NodeFeatures { + staging_runtime_impl::node_features::() + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index fcead1dd0b53..587a6c9a5905 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -114,6 +114,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api", default-features = false } [dev-dependencies] hex-literal = "0.4.1" @@ -227,6 +228,7 @@ std = [ "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] runtime-benchmarks = [ diff --git a/polkadot/runtime/westend/src/impls.rs b/polkadot/runtime/westend/src/impls.rs index 71e6b696a20a..d8741c939a50 100644 --- a/polkadot/runtime/westend/src/impls.rs +++ b/polkadot/runtime/westend/src/impls.rs @@ -167,11 +167,16 @@ where }, ]); + let encoded_versioned_xcm = + VersionedXcm::V4(program).encode().try_into().map_err(|error| { + log::error!(target: "runtime::on_reap_identity", "XCM too large, error: {:?}", error); + pallet_xcm::Error::::XcmTooLarge + })?; // send - let _ = >::send( + let _ = >::send_blob( RawOrigin::Root.into(), Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedXcm::V4(program)), + encoded_versioned_xcm, )?; Ok(()) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 664044b713ee..e6381513170f 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -35,7 +35,7 @@ use frame_support::{ InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, ProcessMessage, ProcessMessageError, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter}, + weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -62,7 +62,7 @@ use runtime_common::{ LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedLocationConverter, }, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, - traits::Leaser, + traits::{Leaser, OnSwap}, BalanceToU256, BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance, }; @@ -101,11 +101,13 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use xcm::{ - latest::{InteriorLocation, Junction, Junction::PalletInstance}, - VersionedLocation, + latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, }; use xcm_builder::PayOverXcm; +use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; + pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase}; @@ -1188,6 +1190,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = MessageQueueHeapSize; type MaxStale = MessageQueueMaxStale; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; #[cfg(not(feature = "runtime-benchmarks"))] type MessageProcessor = MessageProcessor; #[cfg(feature = "runtime-benchmarks")] @@ -1302,7 +1305,7 @@ impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type OnSwap = (Crowdloan, Slots); + type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; @@ -1414,6 +1417,14 @@ impl pallet_asset_rate::Config for Runtime { type BenchmarkHelper = runtime_common::impls::benchmarks::AssetRateArguments; } +// Notify `coretime` pallet when a lease swap occurs +pub struct SwapLeases; +impl OnSwap for SwapLeases { + fn on_swap(one: ParaId, other: ParaId) { + coretime::Pallet::::on_legacy_lease_swap(one, other); + } +} + #[frame_support::runtime(legacy_ordering)] mod runtime { #[runtime::runtime] @@ -1659,11 +1670,11 @@ pub mod migrations { let now = frame_system::Pallet::::block_number(); let lease = slots::Pallet::::lease(para); if lease.is_empty() { - return None + return None; } // Lease not yet started, ignore: if lease.iter().any(Option::is_none) { - return None + return None; } let (index, _) = as Leaser>::lease_period_index(now)?; @@ -1685,7 +1696,7 @@ pub mod migrations { fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)"); - return Ok(Vec::new()) + return Ok(Vec::new()); } log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state"); @@ -1714,7 +1725,7 @@ pub mod migrations { fn on_runtime_upgrade() -> Weight { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!("Skipping session keys upgrade: already applied"); - return ::DbWeight::get().reads(1) + return ::DbWeight::get().reads(1); } log::info!("Upgrading session keys"); Session::upgrade_keys::(transform_session_keys); @@ -1727,7 +1738,7 @@ pub mod migrations { ) -> Result<(), sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)"); - return Ok(()) + return Ok(()); } let key_ids = SessionKeys::key_ids(); @@ -2324,6 +2335,37 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + Ok([VersionedAssetId::V4(xcm_config::TokenLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::TokenLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + impl pallet_nomination_pools_runtime_api::NominationPoolsApi< Block, AccountId, @@ -2642,7 +2684,7 @@ mod remote_tests { #[tokio::test] async fn run_migrations() { if var("RUN_MIGRATION_TESTS").is_err() { - return + return; } sp_tracing::try_init_simple(); diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index 9f9963160590..bdd599d2b752 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -21,7 +21,6 @@ use std::collections::HashSet; use crate::*; use frame_support::traits::WhitelistedStorageKeys; use sp_core::hexdisplay::HexDisplay; -use xcm::latest::prelude::*; #[test] fn remove_keys_weight_is_sensible() { diff --git a/polkadot/runtime/westend/src/weights/pallet_staking.rs b/polkadot/runtime/westend/src/weights/pallet_staking.rs index 7a641e36a126..393fa0b37176 100644 --- a/polkadot/runtime/westend/src/weights/pallet_staking.rs +++ b/polkadot/runtime/westend/src/weights/pallet_staking.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_staking` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-01-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -50,22 +50,22 @@ pub struct WeightInfo(PhantomData); impl pallet_staking::WeightInfo for WeightInfo { /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - /// Storage: `Staking::Ledger` (r:0 w:1) - /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:0 w:1) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `894` + // Measured: `1009` // Estimated: `4764` - // Minimum execution time: 37_340_000 picoseconds. - Weight::from_parts(38_930_000, 0) + // Minimum execution time: 40_585_000 picoseconds. + Weight::from_parts(41_800_000, 0) .saturating_add(Weight::from_parts(0, 4764)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Staking::Bonded` (r:1 w:0) @@ -84,22 +84,22 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1921` // Estimated: `8877` - // Minimum execution time: 80_630_000 picoseconds. - Weight::from_parts(82_196_000, 0) + // Minimum execution time: 81_809_000 picoseconds. + Weight::from_parts(84_387_000, 0) .saturating_add(Weight::from_parts(0, 8877)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) @@ -112,43 +112,45 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2128` // Estimated: `8877` - // Minimum execution time: 83_523_000 picoseconds. - Weight::from_parts(86_639_000, 0) + // Minimum execution time: 89_419_000 picoseconds. + Weight::from_parts(91_237_000, 0) .saturating_add(Weight::from_parts(0, 8877)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Staking::CurrentEra` (r:1 w:0) - /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1075` + // Measured: `1223` // Estimated: `4764` - // Minimum execution time: 38_636_000 picoseconds. - Weight::from_parts(40_399_283, 0) + // Minimum execution time: 45_152_000 picoseconds. + Weight::from_parts(46_460_819, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 869 - .saturating_add(Weight::from_parts(37_752, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Standard Error: 972 + .saturating_add(Weight::from_parts(55_473, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:1) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Staking::Bonded` (r:1 w:1) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) @@ -174,11 +176,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2127 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 81_301_000 picoseconds. - Weight::from_parts(88_609_205, 0) + // Minimum execution time: 82_762_000 picoseconds. + Weight::from_parts(91_035_077, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 3_388 - .saturating_add(Weight::from_parts(1_253_692, 0).saturating_mul(s.into())) + // Standard Error: 3_771 + .saturating_add(Weight::from_parts(1_217_871, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().writes(11)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -186,6 +188,8 @@ impl pallet_staking::WeightInfo for WeightInfo { } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::MinValidatorBond` (r:1 w:0) /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MinCommission` (r:1 w:0) @@ -196,8 +200,6 @@ impl pallet_staking::WeightInfo for WeightInfo { /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:1 w:1) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:1 w:1) @@ -210,33 +212,37 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1301` // Estimated: `4556` - // Minimum execution time: 47_292_000 picoseconds. - Weight::from_parts(48_566_000, 0) + // Minimum execution time: 50_555_000 picoseconds. + Weight::from_parts(52_052_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:128 w:128) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1243 + k * (569 ±0)` + // Measured: `1778 + k * (572 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 28_840_000 picoseconds. - Weight::from_parts(27_510_817, 0) + // Minimum execution time: 35_037_000 picoseconds. + Weight::from_parts(35_081_878, 0) .saturating_add(Weight::from_parts(0, 4556)) - // Standard Error: 6_603 - .saturating_add(Weight::from_parts(6_268_853, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Standard Error: 5_473 + .saturating_add(Weight::from_parts(6_667_924, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -247,8 +253,6 @@ impl pallet_staking::WeightInfo for WeightInfo { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:2 w:2) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:1 w:1) @@ -262,11 +266,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1797 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 57_537_000 picoseconds. - Weight::from_parts(55_854_233, 0) + // Minimum execution time: 62_098_000 picoseconds. + Weight::from_parts(60_154_061, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 14_427 - .saturating_add(Weight::from_parts(3_844_957, 0).saturating_mul(n.into())) + // Standard Error: 19_257 + .saturating_add(Weight::from_parts(3_839_855, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6)) @@ -274,6 +278,8 @@ impl pallet_staking::WeightInfo for WeightInfo { } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -288,12 +294,12 @@ impl pallet_staking::WeightInfo for WeightInfo { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1581` + // Measured: `1747` // Estimated: `6248` - // Minimum execution time: 49_997_000 picoseconds. - Weight::from_parts(51_266_000, 0) + // Minimum execution time: 54_993_000 picoseconds. + Weight::from_parts(56_698_000, 0) .saturating_add(Weight::from_parts(0, 6248)) - .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `Staking::Ledger` (r:1 w:0) @@ -306,40 +312,40 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `865` // Estimated: `4556` - // Minimum execution time: 15_342_000 picoseconds. - Weight::from_parts(15_970_000, 0) + // Minimum execution time: 18_100_000 picoseconds. + Weight::from_parts(18_547_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Staking::Payee` (r:1 w:1) - /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Payee` (r:1 w:1) + /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_payee() -> Weight { // Proof Size summary in bytes: // Measured: `932` // Estimated: `4556` - // Minimum execution time: 20_719_000 picoseconds. - Weight::from_parts(21_373_000, 0) + // Minimum execution time: 23_428_000 picoseconds. + Weight::from_parts(24_080_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Staking::Ledger` (r:1 w:2) + /// Storage: `Staking::Ledger` (r:2 w:2) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) fn set_controller() -> Weight { // Proof Size summary in bytes: // Measured: `865` - // Estimated: `4556` - // Minimum execution time: 18_237_000 picoseconds. - Weight::from_parts(18_896_000, 0) - .saturating_add(Weight::from_parts(0, 4556)) - .saturating_add(T::DbWeight::get().reads(2)) + // Estimated: `8122` + // Minimum execution time: 21_159_000 picoseconds. + Weight::from_parts(21_706_000, 0) + .saturating_add(Weight::from_parts(0, 8122)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Staking::ValidatorCount` (r:0 w:1) @@ -348,8 +354,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_946_000 picoseconds. - Weight::from_parts(2_131_000, 0) + // Minimum execution time: 1_910_000 picoseconds. + Weight::from_parts(2_003_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -359,8 +365,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_840_000 picoseconds. - Weight::from_parts(7_208_000, 0) + // Minimum execution time: 7_076_000 picoseconds. + Weight::from_parts(7_349_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -370,8 +376,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_812_000 picoseconds. - Weight::from_parts(7_254_000, 0) + // Minimum execution time: 7_067_000 picoseconds. + Weight::from_parts(7_389_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -381,8 +387,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_787_000 picoseconds. - Weight::from_parts(7_206_000, 0) + // Minimum execution time: 7_148_000 picoseconds. + Weight::from_parts(7_446_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -393,32 +399,32 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_045_000 picoseconds. - Weight::from_parts(2_281_841, 0) + // Minimum execution time: 2_025_000 picoseconds. + Weight::from_parts(2_229_953, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 70 - .saturating_add(Weight::from_parts(11_592, 0).saturating_mul(v.into())) + // Standard Error: 67 + .saturating_add(Weight::from_parts(11_785, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Staking::Ledger` (r:751 w:1502) + /// Storage: `Staking::Ledger` (r:1502 w:1502) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:751 w:751) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:751 w:0) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:0 w:751) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 751]`. fn deprecate_controller_batch(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `668 + i * (148 ±0)` - // Estimated: `990 + i * (3566 ±0)` - // Minimum execution time: 1_657_000 picoseconds. - Weight::from_parts(1_702_000, 0) + // Measured: `680 + i * (227 ±0)` + // Estimated: `990 + i * (7132 ±0)` + // Minimum execution time: 4_321_000 picoseconds. + Weight::from_parts(4_407_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 20_041 - .saturating_add(Weight::from_parts(13_165_254, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into()))) + // Standard Error: 37_239 + .saturating_add(Weight::from_parts(21_300_598, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into())) } /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -453,11 +459,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2127 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 78_774_000 picoseconds. - Weight::from_parts(85_770_713, 0) + // Minimum execution time: 78_908_000 picoseconds. + Weight::from_parts(84_886_373, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 2_815 - .saturating_add(Weight::from_parts(1_244_494, 0).saturating_mul(s.into())) + // Standard Error: 3_376 + .saturating_add(Weight::from_parts(1_217_850, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -470,11 +476,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `66639` // Estimated: `70104` - // Minimum execution time: 129_905_000 picoseconds. - Weight::from_parts(932_195_554, 0) + // Minimum execution time: 136_389_000 picoseconds. + Weight::from_parts(1_207_241_524, 0) .saturating_add(Weight::from_parts(0, 70104)) - // Standard Error: 57_492 - .saturating_add(Weight::from_parts(4_826_754, 0).saturating_mul(s.into())) + // Standard Error: 77_138 + .saturating_add(Weight::from_parts(6_443_948, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -511,11 +517,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `8249 + n * (396 ±0)` // Estimated: `10779 + n * (3774 ±0)` - // Minimum execution time: 127_094_000 picoseconds. - Weight::from_parts(160_088_053, 0) + // Minimum execution time: 130_222_000 picoseconds. + Weight::from_parts(167_236_150, 0) .saturating_add(Weight::from_parts(0, 10779)) - // Standard Error: 32_978 - .saturating_add(Weight::from_parts(39_845_710, 0).saturating_mul(n.into())) + // Standard Error: 34_051 + .saturating_add(Weight::from_parts(39_899_917, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4)) @@ -539,11 +545,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1922 + l * (5 ±0)` // Estimated: `8877` - // Minimum execution time: 75_672_000 picoseconds. - Weight::from_parts(78_708_335, 0) + // Minimum execution time: 79_136_000 picoseconds. + Weight::from_parts(82_129_497, 0) .saturating_add(Weight::from_parts(0, 8877)) - // Standard Error: 3_387 - .saturating_add(Weight::from_parts(37_084, 0).saturating_mul(l.into())) + // Standard Error: 3_867 + .saturating_add(Weight::from_parts(75_156, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -578,11 +584,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2127 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 87_991_000 picoseconds. - Weight::from_parts(90_272_005, 0) + // Minimum execution time: 89_375_000 picoseconds. + Weight::from_parts(91_224_907, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 2_815 - .saturating_add(Weight::from_parts(1_232_322, 0).saturating_mul(s.into())) + // Standard Error: 3_424 + .saturating_add(Weight::from_parts(1_219_542, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(11)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -627,14 +633,14 @@ impl pallet_staking::WeightInfo for WeightInfo { fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (716 ±0) + v * (3594 ±0)` - // Estimated: `456136 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 528_862_000 picoseconds. - Weight::from_parts(534_620_000, 0) + // Estimated: `456136 + n * (3566 ±4) + v * (3566 ±0)` + // Minimum execution time: 520_905_000 picoseconds. + Weight::from_parts(523_771_000, 0) .saturating_add(Weight::from_parts(0, 456136)) - // Standard Error: 2_005_553 - .saturating_add(Weight::from_parts(65_586_008, 0).saturating_mul(v.into())) - // Standard Error: 199_842 - .saturating_add(Weight::from_parts(18_155_389, 0).saturating_mul(n.into())) + // Standard Error: 2_142_714 + .saturating_add(Weight::from_parts(68_631_588, 0).saturating_mul(v.into())) + // Standard Error: 213_509 + .saturating_add(Weight::from_parts(19_343_025, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(184)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -665,13 +671,13 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `3108 + n * (907 ±0) + v * (391 ±0)` // Estimated: `456136 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 33_532_110_000 picoseconds. - Weight::from_parts(33_926_321_000, 0) + // Minimum execution time: 36_848_619_000 picoseconds. + Weight::from_parts(37_362_442_000, 0) .saturating_add(Weight::from_parts(0, 456136)) - // Standard Error: 374_134 - .saturating_add(Weight::from_parts(4_627_629, 0).saturating_mul(v.into())) - // Standard Error: 374_134 - .saturating_add(Weight::from_parts(4_068_168, 0).saturating_mul(n.into())) + // Standard Error: 415_031 + .saturating_add(Weight::from_parts(5_204_987, 0).saturating_mul(v.into())) + // Standard Error: 415_031 + .saturating_add(Weight::from_parts(4_132_636, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(179)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -688,11 +694,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `946 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_395_956_000 picoseconds. - Weight::from_parts(88_416_870, 0) + // Minimum execution time: 2_512_817_000 picoseconds. + Weight::from_parts(119_401_374, 0) .saturating_add(Weight::from_parts(0, 3510)) - // Standard Error: 8_731 - .saturating_add(Weight::from_parts(4_750_956, 0).saturating_mul(v.into())) + // Standard Error: 8_463 + .saturating_add(Weight::from_parts(4_860_364, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -715,8 +721,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_761_000 picoseconds. - Weight::from_parts(4_013_000, 0) + // Minimum execution time: 3_686_000 picoseconds. + Weight::from_parts(3_881_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -738,8 +744,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_325_000 picoseconds. - Weight::from_parts(3_519_000, 0) + // Minimum execution time: 3_143_000 picoseconds. + Weight::from_parts(3_424_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -769,8 +775,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1870` // Estimated: `6248` - // Minimum execution time: 63_583_000 picoseconds. - Weight::from_parts(65_917_000, 0) + // Minimum execution time: 66_946_000 picoseconds. + Weight::from_parts(69_382_000, 0) .saturating_add(Weight::from_parts(0, 6248)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(6)) @@ -783,8 +789,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `658` // Estimated: `3510` - // Minimum execution time: 10_975_000 picoseconds. - Weight::from_parts(11_328_000, 0) + // Minimum execution time: 11_278_000 picoseconds. + Weight::from_parts(11_603_000, 0) .saturating_add(Weight::from_parts(0, 3510)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -795,9 +801,29 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_954_000 picoseconds. - Weight::from_parts(2_081_000, 0) + // Minimum execution time: 1_963_000 picoseconds. + Weight::from_parts(2_077_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:1) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + fn restore_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `1014` + // Estimated: `4764` + // Minimum execution time: 40_258_000 picoseconds. + Weight::from_parts(41_210_000, 0) + .saturating_add(Weight::from_parts(0, 4764)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/polkadot/runtime/westend/src/weights/pallet_xcm.rs b/polkadot/runtime/westend/src/weights/pallet_xcm.rs index 10725cecf249..80bc551ba1e2 100644 --- a/polkadot/runtime/westend/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/westend/src/weights/pallet_xcm.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -60,8 +60,26 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `147` // Estimated: `3612` - // Minimum execution time: 25_725_000 picoseconds. - Weight::from_parts(26_174_000, 0) + // Minimum execution time: 24_535_000 picoseconds. + Weight::from_parts(25_618_000, 0) + .saturating_add(Weight::from_parts(0, 3612)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 25_376_000 picoseconds. + Weight::from_parts(26_180_000, 0) .saturating_add(Weight::from_parts(0, 3612)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -80,8 +98,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `250` // Estimated: `6196` - // Minimum execution time: 113_140_000 picoseconds. - Weight::from_parts(116_204_000, 0) + // Minimum execution time: 108_786_000 picoseconds. + Weight::from_parts(112_208_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -100,8 +118,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `302` // Estimated: `6196` - // Minimum execution time: 108_571_000 picoseconds. - Weight::from_parts(110_650_000, 0) + // Minimum execution time: 105_190_000 picoseconds. + Weight::from_parts(107_140_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -120,8 +138,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `250` // Estimated: `6196` - // Minimum execution time: 111_836_000 picoseconds. - Weight::from_parts(114_435_000, 0) + // Minimum execution time: 109_027_000 picoseconds. + Weight::from_parts(111_404_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -136,14 +154,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `XcmPallet::SupportedVersion` (r:0 w:1) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_160_000 picoseconds. - Weight::from_parts(7_477_000, 0) + // Minimum execution time: 6_668_000 picoseconds. + Weight::from_parts(7_013_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -151,8 +179,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_934_000 picoseconds. - Weight::from_parts(2_053_000, 0) + // Minimum execution time: 1_740_000 picoseconds. + Weight::from_parts(1_884_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -173,8 +201,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `147` // Estimated: `3612` - // Minimum execution time: 31_123_000 picoseconds. - Weight::from_parts(31_798_000, 0) + // Minimum execution time: 30_200_000 picoseconds. + Weight::from_parts(30_768_000, 0) .saturating_add(Weight::from_parts(0, 3612)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) @@ -195,8 +223,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `327` // Estimated: `3792` - // Minimum execution time: 35_175_000 picoseconds. - Weight::from_parts(36_098_000, 0) + // Minimum execution time: 33_928_000 picoseconds. + Weight::from_parts(35_551_000, 0) .saturating_add(Weight::from_parts(0, 3792)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -207,8 +235,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_974_000 picoseconds. - Weight::from_parts(2_096_000, 0) + // Minimum execution time: 1_759_000 picoseconds. + Weight::from_parts(1_880_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -218,8 +246,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `22` // Estimated: `13387` - // Minimum execution time: 16_626_000 picoseconds. - Weight::from_parts(17_170_000, 0) + // Minimum execution time: 16_507_000 picoseconds. + Weight::from_parts(17_219_000, 0) .saturating_add(Weight::from_parts(0, 13387)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -230,8 +258,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `26` // Estimated: `13391` - // Minimum execution time: 16_937_000 picoseconds. - Weight::from_parts(17_447_000, 0) + // Minimum execution time: 16_633_000 picoseconds. + Weight::from_parts(16_889_000, 0) .saturating_add(Weight::from_parts(0, 13391)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -242,8 +270,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `40` // Estimated: `15880` - // Minimum execution time: 19_157_000 picoseconds. - Weight::from_parts(19_659_000, 0) + // Minimum execution time: 19_297_000 picoseconds. + Weight::from_parts(19_820_000, 0) .saturating_add(Weight::from_parts(0, 15880)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -261,8 +289,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `183` // Estimated: `6123` - // Minimum execution time: 30_699_000 picoseconds. - Weight::from_parts(31_537_000, 0) + // Minimum execution time: 30_364_000 picoseconds. + Weight::from_parts(31_122_000, 0) .saturating_add(Weight::from_parts(0, 6123)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -273,8 +301,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `69` // Estimated: `10959` - // Minimum execution time: 12_303_000 picoseconds. - Weight::from_parts(12_670_000, 0) + // Minimum execution time: 11_997_000 picoseconds. + Weight::from_parts(12_392_000, 0) .saturating_add(Weight::from_parts(0, 10959)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -284,8 +312,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `33` // Estimated: `13398` - // Minimum execution time: 17_129_000 picoseconds. - Weight::from_parts(17_668_000, 0) + // Minimum execution time: 16_894_000 picoseconds. + Weight::from_parts(17_452_000, 0) .saturating_add(Weight::from_parts(0, 13398)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -304,8 +332,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `183` // Estimated: `13548` - // Minimum execution time: 39_960_000 picoseconds. - Weight::from_parts(41_068_000, 0) + // Minimum execution time: 39_864_000 picoseconds. + Weight::from_parts(40_859_000, 0) .saturating_add(Weight::from_parts(0, 13548)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) @@ -318,8 +346,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_333_000 picoseconds. - Weight::from_parts(2_504_000, 0) + // Minimum execution time: 2_363_000 picoseconds. + Weight::from_parts(2_519_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -330,8 +358,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 22_932_000 picoseconds. - Weight::from_parts(23_307_000, 0) + // Minimum execution time: 22_409_000 picoseconds. + Weight::from_parts(22_776_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -342,8 +370,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `23` // Estimated: `3488` - // Minimum execution time: 34_558_000 picoseconds. - Weight::from_parts(35_299_000, 0) + // Minimum execution time: 33_551_000 picoseconds. + Weight::from_parts(34_127_000, 0) .saturating_add(Weight::from_parts(0, 3488)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/xcm/pallet-xcm/Cargo.toml b/polkadot/xcm/pallet-xcm/Cargo.toml index 4840b6127f55..08307c34f8a9 100644 --- a/polkadot/xcm/pallet-xcm/Cargo.toml +++ b/polkadot/xcm/pallet-xcm/Cargo.toml @@ -26,6 +26,7 @@ sp-std = { path = "../../../substrate/primitives/std", default-features = false xcm = { package = "staging-xcm", path = "..", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } +xcm-fee-payment-runtime-api = { path = "../xcm-fee-payment-runtime-api", default-features = false } # marked optional, used in benchmarking frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -54,6 +55,7 @@ std = [ "sp-std/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] runtime-benchmarks = [ diff --git a/polkadot/xcm/pallet-xcm/src/benchmarking.rs b/polkadot/xcm/pallet-xcm/src/benchmarking.rs index ed42f93692b4..e2903d592dc1 100644 --- a/polkadot/xcm/pallet-xcm/src/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm/src/benchmarking.rs @@ -16,6 +16,7 @@ use super::*; use bounded_collections::{ConstU32, WeakBoundedVec}; +use codec::Encode; use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError, BenchmarkResult}; use frame_support::{ traits::fungible::{Inspect, Mutate}, @@ -108,6 +109,21 @@ benchmarks! { let versioned_msg = VersionedXcm::from(msg); }: _>(send_origin, Box::new(versioned_dest), Box::new(versioned_msg)) + send_blob { + let send_origin = + T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) + } + let msg = Xcm::<()>(vec![ClearOrigin]); + let versioned_dest: VersionedLocation = T::reachable_dest().ok_or( + BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)), + )? + .into(); + let versioned_msg = VersionedXcm::from(msg); + let encoded_versioned_msg = versioned_msg.encode().try_into().unwrap(); + }: _>(send_origin, Box::new(versioned_dest), encoded_versioned_msg) + teleport_assets { let (asset, destination) = T::teleportable_asset_and_dest().ok_or( BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)), @@ -227,6 +243,19 @@ benchmarks! { let versioned_msg = VersionedXcm::from(msg); }: _>(execute_origin, Box::new(versioned_msg), Weight::MAX) + execute_blob { + let execute_origin = + T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone()) + .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let msg = Xcm(vec![ClearOrigin]); + if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) + } + let versioned_msg = VersionedXcm::from(msg); + let encoded_versioned_msg = versioned_msg.encode().try_into().unwrap(); + }: _>(execute_origin, encoded_versioned_msg, Weight::MAX) + force_xcm_version { let loc = T::reachable_dest().ok_or( BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)), diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 8a9e5288f2e1..29b61988f73c 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -50,8 +50,8 @@ use sp_runtime::{ use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec}; use xcm::{latest::QueryResponseInfo, prelude::*}; use xcm_builder::{ - ExecuteController, ExecuteControllerWeightInfo, QueryController, QueryControllerWeightInfo, - SendController, SendControllerWeightInfo, + ExecuteController, ExecuteControllerWeightInfo, MaxXcmEncodedSize, QueryController, + QueryControllerWeightInfo, SendController, SendControllerWeightInfo, }; use xcm_executor::{ traits::{ @@ -61,6 +61,7 @@ use xcm_executor::{ }, AssetsInHolding, }; +use xcm_fee_payment_runtime_api::Error as FeePaymentError; #[cfg(any(feature = "try-runtime", test))] use sp_runtime::TryRuntimeError; @@ -86,6 +87,8 @@ pub trait WeightInfo { fn new_query() -> Weight; fn take_response() -> Weight; fn claim_assets() -> Weight; + fn execute_blob() -> Weight; + fn send_blob() -> Weight; } /// fallback implementation @@ -170,6 +173,14 @@ impl WeightInfo for TestWeightInfo { fn claim_assets() -> Weight { Weight::from_parts(100_000_000, 0) } + + fn execute_blob() -> Weight { + Weight::from_parts(100_000_000, 0) + } + + fn send_blob() -> Weight { + Weight::from_parts(100_000_000, 0) + } } #[frame_support::pallet] @@ -285,76 +296,49 @@ pub mod pallet { } impl ExecuteControllerWeightInfo for Pallet { - fn execute() -> Weight { - T::WeightInfo::execute() + fn execute_blob() -> Weight { + T::WeightInfo::execute_blob() } } impl ExecuteController, ::RuntimeCall> for Pallet { type WeightInfo = Self; - fn execute( + fn execute_blob( origin: OriginFor, - message: Box::RuntimeCall>>, + encoded_message: BoundedVec, max_weight: Weight, ) -> Result { - log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight); - let outcome = (|| { - let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; - let mut hash = message.using_encoded(sp_io::hashing::blake2_256); - let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; - let value = (origin_location, message); - ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); - let (origin_location, message) = value; - Ok(T::XcmExecutor::prepare_and_execute( - origin_location, - message, - &mut hash, - max_weight, - max_weight, - )) - })() - .map_err(|e: DispatchError| { - e.with_weight(::execute()) - })?; - - Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); - let weight_used = outcome.weight_used(); - outcome.ensure_complete().map_err(|error| { - log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error); - Error::::LocalExecutionIncomplete.with_weight( - weight_used.saturating_add( - ::execute(), - ), - ) - })?; - Ok(weight_used) + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let message = + VersionedXcm::<::RuntimeCall>::decode(&mut &encoded_message[..]) + .map_err(|error| { + log::error!(target: "xcm::execute_blob", "Unable to decode XCM, error: {:?}", error); + Error::::UnableToDecode + })?; + Self::execute_base(origin_location, Box::new(message), max_weight) } } impl SendControllerWeightInfo for Pallet { - fn send() -> Weight { - T::WeightInfo::send() + fn send_blob() -> Weight { + T::WeightInfo::send_blob() } } impl SendController> for Pallet { type WeightInfo = Self; - fn send( + fn send_blob( origin: OriginFor, dest: Box, - message: Box>, + encoded_message: BoundedVec, ) -> Result { let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; - let interior: Junctions = - origin_location.clone().try_into().map_err(|_| Error::::InvalidOrigin)?; - let dest = Location::try_from(*dest).map_err(|()| Error::::BadVersion)?; - let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; - - let message_id = Self::send_xcm(interior, dest.clone(), message.clone()) - .map_err(Error::::from)?; - let e = Event::Sent { origin: origin_location, destination: dest, message, message_id }; - Self::deposit_event(e); - Ok(message_id) + let message = + VersionedXcm::<()>::decode(&mut &encoded_message[..]).map_err(|error| { + log::error!(target: "xcm::send_blob", "Unable to decode XCM, error: {:?}", error); + Error::::UnableToDecode + })?; + Self::send_base(origin_location, dest, Box::new(message)) } } @@ -561,6 +545,11 @@ pub mod pallet { TooManyReserves, /// Local XCM execution incomplete. LocalExecutionIncomplete, + /// Could not decode XCM. + UnableToDecode, + /// XCM encoded length is too large. + /// Returned when an XCM encoded length is larger than `MaxXcmEncodedSize`. + XcmTooLarge, } impl From for Error { @@ -898,8 +887,64 @@ pub mod pallet { } } + impl Pallet { + /// Underlying logic for both [`execute_blob`] and [`execute`]. + fn execute_base( + origin_location: Location, + message: Box::RuntimeCall>>, + max_weight: Weight, + ) -> Result { + log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight); + let outcome = (|| { + let mut hash = message.using_encoded(sp_io::hashing::blake2_256); + let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; + let value = (origin_location, message); + ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); + let (origin_location, message) = value; + Ok(T::XcmExecutor::prepare_and_execute( + origin_location, + message, + &mut hash, + max_weight, + max_weight, + )) + })() + .map_err(|e: DispatchError| e.with_weight(T::WeightInfo::execute()))?; + + Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); + let weight_used = outcome.weight_used(); + outcome.ensure_complete().map_err(|error| { + log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error); + Error::::LocalExecutionIncomplete + .with_weight(weight_used.saturating_add(T::WeightInfo::execute())) + })?; + Ok(weight_used) + } + + /// Underlying logic for both [`send_blob`] and [`send`]. + fn send_base( + origin_location: Location, + dest: Box, + message: Box>, + ) -> Result { + let interior: Junctions = + origin_location.clone().try_into().map_err(|_| Error::::InvalidOrigin)?; + let dest = Location::try_from(*dest).map_err(|()| Error::::BadVersion)?; + let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; + + let message_id = Self::send_xcm(interior, dest.clone(), message.clone()) + .map_err(Error::::from)?; + let e = Event::Sent { origin: origin_location, destination: dest, message, message_id }; + Self::deposit_event(e); + Ok(message_id) + } + } + #[pallet::call] impl Pallet { + /// WARNING: DEPRECATED. `send` will be removed after June 2024. Use `send_blob` instead. + #[allow(deprecated)] + #[deprecated(note = "`send` will be removed after June 2024. Use `send_blob` instead.")] #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::send())] pub fn send( @@ -907,7 +952,8 @@ pub mod pallet { dest: Box, message: Box>, ) -> DispatchResult { - >::send(origin, dest, message)?; + let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; + Self::send_base(origin_location, dest, message)?; Ok(()) } @@ -1030,6 +1076,13 @@ pub mod pallet { /// No more than `max_weight` will be used in its attempted execution. If this is less than /// the maximum amount of weight that the message could take to be executed, then no /// execution attempt will be made. + /// + /// WARNING: DEPRECATED. `execute` will be removed after June 2024. Use `execute_blob` + /// instead. + #[allow(deprecated)] + #[deprecated( + note = "`execute` will be removed after June 2024. Use `execute_blob` instead." + )] #[pallet::call_index(3)] #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))] pub fn execute( @@ -1037,8 +1090,8 @@ pub mod pallet { message: Box::RuntimeCall>>, max_weight: Weight, ) -> DispatchResultWithPostInfo { - let weight_used = - >::execute(origin, message, max_weight)?; + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let weight_used = Self::execute_base(origin_location, message, max_weight)?; Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into()) } @@ -1449,6 +1502,48 @@ pub mod pallet { })?; Ok(()) } + + /// Execute an XCM from a local, signed, origin. + /// + /// An event is deposited indicating whether the message could be executed completely + /// or only partially. + /// + /// No more than `max_weight` will be used in its attempted execution. If this is less than + /// the maximum amount of weight that the message could take to be executed, then no + /// execution attempt will be made. + /// + /// The message is passed in encoded. It needs to be decodable as a [`VersionedXcm`]. + #[pallet::call_index(13)] + #[pallet::weight(T::WeightInfo::execute_blob())] + pub fn execute_blob( + origin: OriginFor, + encoded_message: BoundedVec, + max_weight: Weight, + ) -> DispatchResultWithPostInfo { + let weight_used = >::execute_blob( + origin, + encoded_message, + max_weight, + )?; + Ok(Some(weight_used.saturating_add(T::WeightInfo::execute_blob())).into()) + } + + /// Send an XCM from a local, signed, origin. + /// + /// The destination, `dest`, will receive this message with a `DescendOrigin` instruction + /// that makes the origin of the message be the origin on this system. + /// + /// The message is passed in encoded. It needs to be decodable as a [`VersionedXcm`]. + #[pallet::call_index(14)] + #[pallet::weight(T::WeightInfo::send_blob())] + pub fn send_blob( + origin: OriginFor, + dest: Box, + encoded_message: BoundedVec, + ) -> DispatchResult { + >::send_blob(origin, dest, encoded_message)?; + Ok(()) + } } } @@ -2363,6 +2458,37 @@ impl Pallet { AccountIdConversion::::into_account_truncating(&ID) } + pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + let message = + Xcm::<()>::try_from(message).map_err(|_| FeePaymentError::VersionedConversionFailed)?; + + T::Weigher::weight(&mut message.into()).map_err(|()| { + log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight"); + FeePaymentError::WeightNotComputable + }) + } + + pub fn query_delivery_fees( + destination: VersionedLocation, + message: VersionedXcm<()>, + ) -> Result { + let result_version = destination.identify_version().max(message.identify_version()); + + let destination = + destination.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + + let message = message.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + + let (_, fees) = validate_send::(destination, message).map_err(|error| { + log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error); + FeePaymentError::Unroutable + })?; + + VersionedAssets::from(fees) + .into_version(result_version) + .map_err(|_| FeePaymentError::VersionedConversionFailed) + } + /// Create a new expectation of a query response with the querier being here. fn do_new_query( responder: impl Into, diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 13022d9a8b1f..763d768e154a 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -20,10 +20,10 @@ pub(crate) mod assets_transfer; use crate::{ mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, - ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, - VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, - WeightInfo, + LatestVersionedLocation, Pallet, Queries, QueryStatus, VersionDiscoveryQueue, + VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo, }; +use codec::Encode; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, traits::{Currency, Hooks}, @@ -305,11 +305,12 @@ fn send_works() { ]); let versioned_dest = Box::new(RelayLocation::get().into()); - let versioned_message = Box::new(VersionedXcm::from(message.clone())); - assert_ok!(XcmPallet::send( + let versioned_message = VersionedXcm::from(message.clone()); + let encoded_versioned_message = versioned_message.encode().try_into().unwrap(); + assert_ok!(XcmPallet::send_blob( RuntimeOrigin::signed(ALICE), versioned_dest, - versioned_message + encoded_versioned_message )); let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap())) .into_iter() @@ -341,16 +342,16 @@ fn send_fails_when_xcm_router_blocks() { ]; new_test_ext_with_balances(balances).execute_with(|| { let sender: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); - let message = Xcm(vec![ + let message = Xcm::<()>(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), buy_execution((Parent, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: sender }, ]); assert_noop!( - XcmPallet::send( + XcmPallet::send_blob( RuntimeOrigin::signed(ALICE), Box::new(Location::ancestor(8).into()), - Box::new(VersionedXcm::from(message.clone())), + VersionedXcm::from(message.clone()).encode().try_into().unwrap(), ), crate::Error::::SendFailure ); @@ -371,13 +372,16 @@ fn execute_withdraw_to_deposit_works() { let weight = BaseXcmWeight::get() * 3; let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ WithdrawAsset((Here, SEND_AMOUNT).into()), buy_execution((Here, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -399,18 +403,21 @@ fn trapped_assets_can_be_claimed() { let weight = BaseXcmWeight::get() * 6; let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into(); - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm(vec![ WithdrawAsset((Here, SEND_AMOUNT).into()), buy_execution((Here, SEND_AMOUNT)), // Don't propagated the error into the result. - SetErrorHandler(Xcm(vec![ClearError])), + SetErrorHandler(Xcm::(vec![ClearError])), // This will make an error. Trap(0), // This would succeed, but we never get to it. DepositAsset { assets: AllCounted(1).into(), beneficiary: dest.clone() }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight )); let source: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); @@ -437,13 +444,16 @@ fn trapped_assets_can_be_claimed() { assert_eq!(trapped, expected); let weight = BaseXcmWeight::get() * 3; - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() }, buy_execution((Here, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: dest.clone() }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight )); @@ -453,13 +463,16 @@ fn trapped_assets_can_be_claimed() { // Can't claim twice. assert_err_ignore_postinfo!( - XcmPallet::execute( + XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() }, buy_execution((Here, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight ), Error::::LocalExecutionIncomplete @@ -473,12 +486,13 @@ fn claim_assets_works() { let balances = vec![(ALICE, INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { // First trap some assets. - let trapping_program = - Xcm::builder_unsafe().withdraw_asset((Here, SEND_AMOUNT).into()).build(); + let trapping_program = Xcm::::builder_unsafe() + .withdraw_asset((Here, SEND_AMOUNT).into()) + .build(); // Even though assets are trapped, the extrinsic returns success. - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::V4(trapping_program)), + VersionedXcm::V4(trapping_program).encode().try_into().unwrap(), BaseXcmWeight::get() * 2, )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -531,9 +545,9 @@ fn incomplete_execute_reverts_side_effects() { assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); let amount_to_send = INITIAL_BALANCE - ExistentialDeposit::get(); let assets: Assets = (Here, amount_to_send).into(); - let result = XcmPallet::execute( + let result = XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ // Withdraw + BuyExec + Deposit should work WithdrawAsset(assets.clone()), buy_execution(assets.inner()[0].clone()), @@ -541,7 +555,10 @@ fn incomplete_execute_reverts_side_effects() { // Withdrawing once more will fail because of InsufficientBalance, and we expect to // revert the effects of the above instructions as well WithdrawAsset(assets), - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight, ); // all effects are reverted and balances unchanged for either sender or receiver @@ -553,7 +570,7 @@ fn incomplete_execute_reverts_side_effects() { Err(sp_runtime::DispatchErrorWithPostInfo { post_info: frame_support::dispatch::PostDispatchInfo { actual_weight: Some( - as ExecuteControllerWeightInfo>::execute() + weight + <::WeightInfo>::execute_blob() + weight ), pays_fee: frame_support::dispatch::Pays::Yes, }, diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs index ba8d726aecff..e836486e86e3 100644 --- a/polkadot/xcm/src/lib.rs +++ b/polkadot/xcm/src/lib.rs @@ -48,6 +48,9 @@ mod tests; /// Maximum nesting level for XCM decoding. pub const MAX_XCM_DECODE_DEPTH: u32 = 8; +/// Maximum encoded size. +/// See `decoding_respects_limit` test for more reasoning behind this value. +pub const MAX_XCM_ENCODED_SIZE: u32 = 12402; /// A version of XCM. pub type Version = u32; @@ -443,6 +446,16 @@ impl IntoVersion for VersionedXcm { } } +impl IdentifyVersion for VersionedXcm { + fn identify_version(&self) -> Version { + match self { + Self::V2(_) => v2::VERSION, + Self::V3(_) => v3::VERSION, + Self::V4(_) => v4::VERSION, + } + } +} + impl From> for VersionedXcm { fn from(x: v2::Xcm) -> Self { VersionedXcm::V2(x) diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs index 30ee485589a2..6635408282e4 100644 --- a/polkadot/xcm/src/v4/mod.rs +++ b/polkadot/xcm/src/v4/mod.rs @@ -1488,7 +1488,21 @@ mod tests { let encoded = big_xcm.encode(); assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - let nested_xcm = Xcm::<()>(vec![ + let mut many_assets = Assets::new(); + for index in 0..MAX_ITEMS_IN_ASSETS { + many_assets.push((GeneralIndex(index as u128), 1u128).into()); + } + + let full_xcm_pass = + Xcm::<()>(vec![ + TransferAsset { assets: many_assets, beneficiary: Here.into() }; + MAX_INSTRUCTIONS_TO_DECODE as usize + ]); + let encoded = full_xcm_pass.encode(); + assert_eq!(encoded.len(), 12402); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok()); + + let nested_xcm_fail = Xcm::<()>(vec![ DepositReserveAsset { assets: All.into(), dest: Here.into(), @@ -1496,10 +1510,10 @@ mod tests { }; (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize ]); - let encoded = nested_xcm.encode(); + let encoded = nested_xcm_fail.encode(); assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]); + let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm_fail); 64]); let encoded = even_more_nested_xcm.encode(); assert_eq!(encoded.len(), 342530); // This should not decode since the limit is 100 diff --git a/polkadot/xcm/xcm-builder/src/controller.rs b/polkadot/xcm/xcm-builder/src/controller.rs index 04b19eaa5870..6bdde2a967de 100644 --- a/polkadot/xcm/xcm-builder/src/controller.rs +++ b/polkadot/xcm/xcm-builder/src/controller.rs @@ -21,6 +21,7 @@ use frame_support::{ dispatch::{DispatchErrorWithPostInfo, WithPostDispatchInfo}, pallet_prelude::DispatchError, + parameter_types, BoundedVec, }; use sp_std::boxed::Box; use xcm::prelude::*; @@ -41,8 +42,12 @@ impl Controller f /// Weight functions needed for [`ExecuteController`]. pub trait ExecuteControllerWeightInfo { - /// Weight for [`ExecuteController::execute`] - fn execute() -> Weight; + /// Weight for [`ExecuteController::execute_blob`] + fn execute_blob() -> Weight; +} + +parameter_types! { + pub const MaxXcmEncodedSize: u32 = xcm::MAX_XCM_ENCODED_SIZE; } /// Execute an XCM locally, for a given origin. @@ -61,19 +66,19 @@ pub trait ExecuteController { /// # Parameters /// /// - `origin`: the origin of the call. - /// - `message`: the XCM program to be executed. + /// - `msg`: the encoded XCM to be executed, should be decodable as a [`VersionedXcm`] /// - `max_weight`: the maximum weight that can be consumed by the execution. - fn execute( + fn execute_blob( origin: Origin, - message: Box>, + message: BoundedVec, max_weight: Weight, ) -> Result; } /// Weight functions needed for [`SendController`]. pub trait SendControllerWeightInfo { - /// Weight for [`SendController::send`] - fn send() -> Weight; + /// Weight for [`SendController::send_blob`] + fn send_blob() -> Weight; } /// Send an XCM from a given origin. @@ -93,11 +98,11 @@ pub trait SendController { /// /// - `origin`: the origin of the call. /// - `dest`: the destination of the message. - /// - `msg`: the XCM to be sent. - fn send( + /// - `msg`: the encoded XCM to be sent, should be decodable as a [`VersionedXcm`] + fn send_blob( origin: Origin, dest: Box, - message: Box>, + message: BoundedVec, ) -> Result; } @@ -137,35 +142,35 @@ pub trait QueryController: QueryHandler { impl ExecuteController for () { type WeightInfo = (); - fn execute( + fn execute_blob( _origin: Origin, - _message: Box>, + _message: BoundedVec, _max_weight: Weight, ) -> Result { - Err(DispatchError::Other("ExecuteController::execute not implemented") + Err(DispatchError::Other("ExecuteController::execute_blob not implemented") .with_weight(Weight::zero())) } } impl ExecuteControllerWeightInfo for () { - fn execute() -> Weight { + fn execute_blob() -> Weight { Weight::zero() } } impl SendController for () { type WeightInfo = (); - fn send( + fn send_blob( _origin: Origin, _dest: Box, - _message: Box>, + _message: BoundedVec, ) -> Result { Ok(Default::default()) } } impl SendControllerWeightInfo for () { - fn send() -> Weight { + fn send_blob() -> Weight { Weight::zero() } } diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index e2af8187136e..46d0ad227bfd 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -43,7 +43,7 @@ pub use barriers::{ mod controller; pub use controller::{ - Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController, + Controller, ExecuteController, ExecuteControllerWeightInfo, MaxXcmEncodedSize, QueryController, QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo, }; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml new file mode 100644 index 000000000000..682642d13c3a --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "xcm-fee-payment-runtime-api" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +repository.workspace = true +description = "XCM fee payment runtime API" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } + +sp-api = { path = "../../../substrate/primitives/api", default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", + "serde", +] } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-weights = { path = "../../../substrate/primitives/weights", default-features = false } +xcm = { package = "staging-xcm", path = "../", default-features = false } +frame-support = { path = "../../../substrate/frame/support", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "scale-info/std", + "sp-api/std", + "sp-runtime/std", + "sp-std/std", + "sp-weights/std", + "xcm/std", +] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs new file mode 100644 index 000000000000..20bf9236f1fb --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -0,0 +1,99 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Runtime API definition for xcm transaction payment. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TypeInfo; +use sp_std::vec::Vec; +use sp_weights::Weight; +use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; + +sp_api::decl_runtime_apis! { + /// A trait of XCM payment API. + /// + /// API provides functionality for obtaining: + /// + /// * the weight required to execute an XCM message, + /// * a list of acceptable `AssetId`s for message execution payment, + /// * the cost of the weight in the specified acceptable `AssetId`. + /// * the fees for an XCM message delivery. + /// + /// To determine the execution weight of the calls required for + /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. + pub trait XcmPaymentApi { + /// Returns a list of acceptable payment assets. + /// + /// # Arguments + /// + /// * `xcm_version`: Version. + fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; + + /// Returns a weight needed to execute a XCM. + /// + /// # Arguments + /// + /// * `message`: `VersionedXcm`. + fn query_xcm_weight(message: VersionedXcm<()>) -> Result; + + /// Converts a weight into a fee for the specified `AssetId`. + /// + /// # Arguments + /// + /// * `weight`: convertible `Weight`. + /// * `asset`: `VersionedAssetId`. + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + + /// Get delivery fees for sending a specific `message` to a `destination`. + /// These always come in a specific asset, defined by the chain. + /// + /// # Arguments + /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the + /// size of the message. + /// * `destination`: The destination to send the message to. Different destinations may use + /// different senders that charge different fees. + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + } +} + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// An API part is unsupported. + #[codec(index = 0)] + Unimplemented, + + /// Converting a versioned data structure from one version to another failed. + #[codec(index = 1)] + VersionedConversionFailed, + + /// XCM message weight calculation failed. + #[codec(index = 2)] + WeightNotComputable, + + /// XCM version not able to be handled. + #[codec(index = 3)] + UnhandledXcmVersion, + + /// The given asset is not handled as a fee asset. + #[codec(index = 4)] + AssetNotFound, + + /// Destination is known to be unroutable. + #[codec(index = 5)] + Unroutable, +} diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs index 377c77f30a47..286d0038e187 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs @@ -275,6 +275,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = MessageQueueHeapSize; type MaxStale = MessageQueueMaxStale; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); type MessageProcessor = MessageProcessor; type QueueChangeHandler = (); type QueuePausedQuery = (); diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 3224df66cbe5..6790b535d169 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -232,6 +232,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = MessageQueueHeapSize; type MaxStale = MessageQueueMaxStale; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); #[cfg(not(feature = "runtime-benchmarks"))] type MessageProcessor = MessageProcessor; #[cfg(feature = "runtime-benchmarks")] diff --git a/prdoc/pr_3607.prdoc b/prdoc/pr_3607.prdoc new file mode 100644 index 000000000000..1a69b25ad255 --- /dev/null +++ b/prdoc/pr_3607.prdoc @@ -0,0 +1,26 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "XCM fee payment API" + +doc: + - audience: Runtime Dev + description: | + A runtime API was added for estimating the fees required for XCM execution and delivery. + This is the basic building block needed for UIs to accurately estimate fees. + An example implementation is shown in the PR. Ideally it's simple to implement, you only need to call existing parts of your XCM config. + The API looks like so: + ```rust + fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; + fn query_xcm_weight(message: VersionedXcm) -> Result; + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + ``` + The first three relate to XCM execution fees, given an XCM, you can query its weight, then which assets are acceptable for buying weight and convert weight to a number of those assets. + The last one takes in a destination and a message you want to send from the runtime you're executing this on, it will give you the delivery fees. + +crates: + - name: xcm-fee-payment-runtime-api + - name: rococo-runtime + - name: westend-runtime + diff --git a/prdoc/pr_3706.prdoc b/prdoc/pr_3706.prdoc new file mode 100644 index 000000000000..edeb08241bed --- /dev/null +++ b/prdoc/pr_3706.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Extrinsic to restore corrupted staking ledgers + +doc: + - audience: Runtime User + description: | + This PR adds a new extrinsic `Call::restore_ledger ` gated by `StakingAdmin` origin that restores a corrupted staking ledger. This extrinsic will be used to recover ledgers that were affected by the issue discussed in https://github.com/paritytech/polkadot-sdk/issues/3245. + The extrinsic will re-write the storage items associated with a stash account provided as input parameter. The data used to reset the ledger can be either i) fetched on-chain or ii) partially/totally set by the input parameters of the call. + + Changes introduced: + - Adds `Call::restore_ledger ` extrinsic to recover a corrupted ledger; + - Adds trait `frame_support::traits::currency::InspectLockableCurrency` to allow external pallets to read current locks given an account and lock ID; + - Implements the `InspectLockableCurrency` in the pallet-balances. + - Adds staking locks try-runtime checks (https://github.com/paritytech/polkadot-sdk/issues/3751) + +crates: + - name: pallet-staking + - name: pallet-balances diff --git a/prdoc/pr_3714.prdoc b/prdoc/pr_3714.prdoc new file mode 100644 index 000000000000..e276d0d2d373 --- /dev/null +++ b/prdoc/pr_3714.prdoc @@ -0,0 +1,10 @@ +title: Handle legacy lease swaps on coretime + +doc: + - audience: Runtime Dev + description: | + When a `registar::swap` extrinsic is executed it swaps two leases on the relay chain but the + broker chain never knows about this swap. This change notifies the broker chain via a XCM + message for a swap so that it can update its state. +crates: + - name: pallet-broker diff --git a/prdoc/pr_3718.prdoc b/prdoc/pr_3718.prdoc new file mode 100644 index 000000000000..b2b24cc9704d --- /dev/null +++ b/prdoc/pr_3718.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Deprecate scheduler traits v1 and v2 + +doc: + - audience: Runtime Dev + description: | + Add `#[deprecated]` attribute to scheduler traits v1 and v2 to deprecate old versions + +crates: + - name: frame-support + - name: pallet-scheduler diff --git a/prdoc/pr_3749.prdoc b/prdoc/pr_3749.prdoc new file mode 100644 index 000000000000..1ebde9670e0f --- /dev/null +++ b/prdoc/pr_3749.prdoc @@ -0,0 +1,47 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "pallet-xcm: deprecate execute and send in favor of execute_blob and send_blob" + +doc: + - audience: Runtime Dev + description: | + pallet-xcm's extrinsics `execute` and `send` have been marked as deprecated. + Please change their usage to the new `execute_blob` and `send_blob`. + The migration from the old extrinsic to the new is very simple. + If you have your message `xcm: VersionedXcm`, then instead of passing in + `Box::new(xcm)` to both `execute` and `send`, you would pass in + `xcm.encode().try_into()` and handle the potential error of its encoded length + being bigger than `MAX_XCM_ENCODED_SIZE`. + + pallet-contracts takes the XCM encoded now as well. It follows the same API as + `execute_blob` and `send_blob`. + - audience: Runtime User + description: | + pallet-xcm has a new pair of extrinsics, `execute_blob` and `send_blob`. + These are meant to be used instead of `execute` and `send`, which are now deprecated + and will be removed eventually. + These new extrinsics just require you to input the encoded XCM. + There's a new utility in PolkadotJS Apps for encoding XCMs you can use: + https://polkadot.js.org/apps/#/utilities/xcm + Just pass in the encoded XCM to the new extrinsics and you're done. + + pallet-contracts takes the XCM encoded now as well. It follows the same API as + `execute_blob` and `send_blob`. + +crates: +- name: pallet-xcm +- name: staging-xcm +- name: staging-xcm-builder +- name: pallet-contracts +- name: asset-hub-rococo-runtime +- name: asset-hub-westend-runtime +- name: bridge-hub-rococo-runtime +- name: bridge-hub-westend-runtime +- name: collectives-westend-runtime +- name: coretime-rococo-runtime +- name: coretime-westend-runtime +- name: people-rococo-runtime +- name: people-westend-runtime +- name: rococo-runtime +- name: westend-runtime diff --git a/prdoc/pr_3795.prdoc b/prdoc/pr_3795.prdoc new file mode 100644 index 000000000000..da01fcbec821 --- /dev/null +++ b/prdoc/pr_3795.prdoc @@ -0,0 +1,14 @@ +title: Enable collators to build on multiple cores + +doc: + - audience: Node Dev + description: | + Introduces a `CoreIndex` parameter in `SubmitCollationParams`. This enables + the collators to make use of potentially multiple cores assigned at some relay + chain block. This extra parameter is used by the collator protocol and collation + generation subsystems to forward the collation to the approapriate backing group. + +crates: +- name: polkadot-node-collation-generation +- name: polkadot-collator-protocol + bump: minor diff --git a/prdoc/pr_3817.prdoc b/prdoc/pr_3817.prdoc new file mode 100644 index 000000000000..bf9d397122f9 --- /dev/null +++ b/prdoc/pr_3817.prdoc @@ -0,0 +1,23 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Parachain Runtime API Implementations into mod apis Refactoring + +doc: + - audience: Runtime Dev + description: | + This PR introduces a refactoring to the runtime API implementations within the parachain template project. The primary changes include enhancing the visibility of `RUNTIME_API_VERSIONS` to `pub` in `impl_runtime_apis.rs`, centralizing API implementations in a new `apis.rs` file, and streamlining `lib.rs`. These changes aim to improve project structure, maintainability, and readability. + + Key Changes: + - `RUNTIME_API_VERSIONS` is now publicly accessible, enhancing module-wide visibility. + - Introduction of `apis.rs` centralizes runtime API implementations, promoting a cleaner and more navigable project structure. + - The main runtime library file, `lib.rs`, has been updated to reflect these structural changes, removing redundant API implementations and simplifying runtime configuration by pointing `VERSION` to the newly exposed `RUNTIME_API_VERSIONS` from `apis.rs`. + + Motivations: + - **Improved Project Structure**: Centralizing API implementations offers a more organized and understandable project layout. + - **Enhanced Readability**: The refactoring efforts aim to declutter `lib.rs`, facilitating easier comprehension for new contributors. + +crates: + - name: sp-api-proc-macro + - name: parachain-template-node + - name: parachain-template-runtime diff --git a/prdoc/pr_3844.prdoc b/prdoc/pr_3844.prdoc new file mode 100644 index 000000000000..a92092f91b20 --- /dev/null +++ b/prdoc/pr_3844.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add the ability for MessageQueue to process enqueued messages on idle + +doc: + - audience: Runtime Dev + description: | + Add the option to use remaining weight on idle for processing enqueued messages. + This will increase the chances of the messages enqueued during inherent extrinsics to be processed in the same block. + New config types is added on the message-queue `Config` trait: + - `IdleMaxServiceWeight` + + example: + ```rust + parameter_types! { + // The maximum weight to be used from remaining weight for processing enqueued messages on idle + pub const IdleMaxServiceWeight: Weight = Some(Weight); + } + + type IdleMaxServiceWeight = IdleMaxServiceWeight; // or `()` to not use this feature + ``` + +crates: + - name: pallet-message-queue diff --git a/prdoc/pr_3849.prdoc b/prdoc/pr_3849.prdoc new file mode 100644 index 000000000000..a1372b60ffc6 --- /dev/null +++ b/prdoc/pr_3849.prdoc @@ -0,0 +1,13 @@ +title: Unrequest a pre-image when it failed to execute + +doc: + - audience: Runtime User + description: | + When a referenda finished the proposal will be scheduled. When it is scheduled, + the pre-image is requested. The pre-image is unrequested after the proposal + was executed. However, if the proposal failed to execute it wasn't unrequested. + Thus, it could not be removed from the on-chain state. This issue is now solved + by ensuring to unrequest the pre-image when it failed to execute. + +crates: + - name: pallet-scheduler diff --git a/prdoc/pr_3850.prdoc b/prdoc/pr_3850.prdoc new file mode 100644 index 000000000000..8f7ce16076e8 --- /dev/null +++ b/prdoc/pr_3850.prdoc @@ -0,0 +1,15 @@ +title: Detect incorrect pre-image length when submitting a referenda + +doc: + - audience: Runtime User + description: | + When submitting a referenda the `proposal` is passed as argument. + The `proposal` is most of the time a reference to a `pre-image` and + which also contains the length of the `pre-image`. This pull request + adds some logic to check that if the `pre-image` already exists and if + it exists, it ensures that the length is passed correctly. This prevents + that the referenda can not be executed because of a mismatch of this + length. + +crates: + - name: pallet-referenda diff --git a/prdoc/schema_user.json b/prdoc/schema_user.json index 1bd0b3b93ee4..593113cf3260 100644 --- a/prdoc/schema_user.json +++ b/prdoc/schema_user.json @@ -132,7 +132,8 @@ } }, "required": [ - "name" + "name", + "bump" ] }, "migration_db": { @@ -187,6 +188,11 @@ "const": "patch", "title": "Patch", "description": "A bump to the third leftmost non-zero digit of the version number." + }, + { + "const": "none", + "title": "None", + "description": "This change requires no SemVer bump (e.g. change was a test or benchmark)." } ] }, diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ca7e14f6eb16..a9606ac0bb75 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1303,6 +1303,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = ConstU32<{ 64 * 1024 }>; type MaxStale = ConstU32<128>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); } parameter_types! { diff --git a/substrate/client/authority-discovery/src/interval.rs b/substrate/client/authority-discovery/src/interval.rs index 23c7ce266e3b..0eee0d159cd8 100644 --- a/substrate/client/authority-discovery/src/interval.rs +++ b/substrate/client/authority-discovery/src/interval.rs @@ -28,6 +28,7 @@ use std::{ /// /// Doubles interval duration on each tick until the configured maximum is reached. pub struct ExpIncInterval { + start: Duration, max: Duration, next: Duration, delay: Delay, @@ -37,14 +38,29 @@ impl ExpIncInterval { /// Create a new [`ExpIncInterval`]. pub fn new(start: Duration, max: Duration) -> Self { let delay = Delay::new(start); - Self { max, next: start * 2, delay } + Self { start, max, next: start * 2, delay } } - /// Fast forward the exponentially increasing interval to the configured maximum. + /// Fast forward the exponentially increasing interval to the configured maximum, if not already + /// set. pub fn set_to_max(&mut self) { + if self.next == self.max { + return; + } + self.next = self.max; self.delay = Delay::new(self.next); } + + /// Rewind the exponentially increasing interval to the configured start, if not already set. + pub fn set_to_start(&mut self) { + if self.next == self.start * 2 { + return; + } + + self.next = self.start * 2; + self.delay = Delay::new(self.start); + } } impl Stream for ExpIncInterval { diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs index b77f0241ec2f..4ad7db5f7da9 100644 --- a/substrate/client/authority-discovery/src/worker.rs +++ b/substrate/client/authority-discovery/src/worker.rs @@ -129,6 +129,9 @@ pub struct Worker { /// List of keys onto which addresses have been published at the latest publication. /// Used to check whether they have changed. latest_published_keys: HashSet, + /// List of the kademlia keys that have been published at the latest publication. + /// Used to associate DHT events with our published records. + latest_published_kad_keys: HashSet, /// Same value as in the configuration. publish_non_global_ips: bool, @@ -265,6 +268,7 @@ where publish_interval, publish_if_changed_interval, latest_published_keys: HashSet::new(), + latest_published_kad_keys: HashSet::new(), publish_non_global_ips: config.publish_non_global_ips, public_addresses, strict_record_validation: config.strict_record_validation, @@ -397,8 +401,17 @@ where self.client.as_ref(), ).await?.into_iter().collect::>(); - if only_if_changed && keys == self.latest_published_keys { - return Ok(()) + if only_if_changed { + // If the authority keys did not change and the `publish_if_changed_interval` was + // triggered then do nothing. + if keys == self.latest_published_keys { + return Ok(()) + } + + // We have detected a change in the authority keys, reset the timers to + // publish and gather data faster. + self.publish_interval.set_to_start(); + self.query_interval.set_to_start(); } let addresses = serialize_addresses(self.addresses_to_publish()); @@ -422,6 +435,8 @@ where keys_vec, )?; + self.latest_published_kad_keys = kv_pairs.iter().map(|(k, _)| k.clone()).collect(); + for (key, value) in kv_pairs.into_iter() { self.network.put_value(key, value); } @@ -523,6 +538,10 @@ where } }, DhtEvent::ValuePut(hash) => { + if !self.latest_published_kad_keys.contains(&hash) { + return; + } + // Fast forward the exponentially increasing interval to the configured maximum. In // case this was the first successful address publishing there is no need for a // timely retry. @@ -535,6 +554,11 @@ where debug!(target: LOG_TARGET, "Successfully put hash '{:?}' on Dht.", hash) }, DhtEvent::ValuePutFailed(hash) => { + if !self.latest_published_kad_keys.contains(&hash) { + // Not a value we have published or received multiple times. + return; + } + if let Some(metrics) = &self.metrics { metrics.dht_event_received.with_label_values(&["value_put_failed"]).inc(); } diff --git a/substrate/frame/balances/src/impl_currency.rs b/substrate/frame/balances/src/impl_currency.rs index 1ac882ade70d..d5fe9934e239 100644 --- a/substrate/frame/balances/src/impl_currency.rs +++ b/substrate/frame/balances/src/impl_currency.rs @@ -28,8 +28,8 @@ use frame_support::{ tokens::{fungible, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort}, Currency, DefensiveSaturating, ExistenceRequirement, ExistenceRequirement::AllowDeath, - Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, - ReservableCurrency, SignedImbalance, TryDrop, WithdrawReasons, + Get, Imbalance, InspectLockableCurrency, LockIdentifier, LockableCurrency, + NamedReservableCurrency, ReservableCurrency, SignedImbalance, TryDrop, WithdrawReasons, }, }; use frame_system::pallet_prelude::BlockNumberFor; @@ -918,3 +918,12 @@ where Self::update_locks(who, &locks[..]); } } + +impl, I: 'static> InspectLockableCurrency for Pallet { + fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance { + Self::locks(who) + .into_iter() + .filter(|l| l.id == id) + .fold(Zero::zero(), |acc, l| acc + l.amount) + } +} diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index bd4ff762c748..450b1a84aa87 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -24,8 +24,8 @@ use frame_support::{ BalanceStatus::{Free, Reserved}, Currency, ExistenceRequirement::{self, AllowDeath, KeepAlive}, - Hooks, LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency, - WithdrawReasons, + Hooks, InspectLockableCurrency, LockIdentifier, LockableCurrency, NamedReservableCurrency, + ReservableCurrency, WithdrawReasons, }, StorageNoopGuard, }; @@ -88,6 +88,24 @@ fn basic_locking_should_work() { }); } +#[test] +fn inspect_lock_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); + Balances::set_lock(ID_2, &1, 10, WithdrawReasons::all()); + Balances::set_lock(ID_1, &2, 20, WithdrawReasons::all()); + + assert_eq!(>::balance_locked(ID_1, &1), 10); + assert_eq!(>::balance_locked(ID_2, &1), 10); + assert_eq!(>::balance_locked(ID_1, &2), 20); + assert_eq!(>::balance_locked(ID_2, &2), 0); + assert_eq!(>::balance_locked(ID_1, &3), 0); + }) +} + #[test] fn account_should_be_reaped() { ExtBuilder::default() diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index 70f488e998cc..98ac074ca917 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -918,6 +918,23 @@ mod benches { Ok(()) } + #[benchmark] + fn swap_leases() -> Result<(), BenchmarkError> { + let admin_origin = + T::AdminOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + // Add two leases in `Leases` + let n = (T::MaxLeasedCores::get() / 2) as usize; + let mut leases = vec![LeaseRecordItem { task: 1, until: 10u32.into() }; n]; + leases.extend(vec![LeaseRecordItem { task: 2, until: 20u32.into() }; n]); + Leases::::put(BoundedVec::try_from(leases).unwrap()); + + #[extrinsic_call] + _(admin_origin as T::RuntimeOrigin, 1, 2); + + Ok(()) + } + // Implements a test for each benchmark. Execute with: // `cargo test -p pallet-broker --features runtime-benchmarks`. impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index f2451013251f..74cda9c4f4cb 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -437,4 +437,22 @@ impl Pallet { Self::deposit_event(Event::AllowedRenewalDropped { core, when }); Ok(()) } + + pub(crate) fn do_swap_leases(id: TaskId, other: TaskId) -> DispatchResult { + let mut id_leases_count = 0; + let mut other_leases_count = 0; + Leases::::mutate(|leases| { + leases.iter_mut().for_each(|lease| { + if lease.task == id { + lease.task = other; + id_leases_count += 1; + } else if lease.task == other { + lease.task = id; + other_leases_count += 1; + } + }) + }); + + Ok(()) + } } diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index f1b49a73a52d..b9b5e309ca91 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -786,5 +786,13 @@ pub mod pallet { Self::do_notify_core_count(core_count)?; Ok(()) } + + #[pallet::call_index(99)] + #[pallet::weight(T::WeightInfo::swap_leases())] + pub fn swap_leases(origin: OriginFor, id: TaskId, other: TaskId) -> DispatchResult { + T::AdminOrigin::ensure_origin_or_root(origin)?; + Self::do_swap_leases(id, other)?; + Ok(()) + } } } diff --git a/substrate/frame/broker/src/weights.rs b/substrate/frame/broker/src/weights.rs index a8f50eeee6e6..a8b9fb598b8b 100644 --- a/substrate/frame/broker/src/weights.rs +++ b/substrate/frame/broker/src/weights.rs @@ -17,10 +17,10 @@ //! Autogenerated weights for `pallet_broker` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-pzhd7p6z-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -76,6 +76,7 @@ pub trait WeightInfo { fn request_revenue_info_at() -> Weight; fn notify_core_count() -> Weight; fn do_tick_base() -> Weight; + fn swap_leases() -> Weight; } /// Weights for `pallet_broker` using the Substrate node and recommended hardware. @@ -87,8 +88,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040_000 picoseconds. - Weight::from_parts(3_344_000, 0) + // Minimum execution time: 2_865_000 picoseconds. + Weight::from_parts(3_061_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -97,8 +98,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 21_259_000 picoseconds. - Weight::from_parts(22_110_000, 7496) + // Minimum execution time: 18_431_000 picoseconds. + Weight::from_parts(19_558_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,8 +109,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 20_330_000 picoseconds. - Weight::from_parts(20_826_000, 7496) + // Minimum execution time: 17_724_000 picoseconds. + Weight::from_parts(18_688_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -119,8 +120,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 13_411_000 picoseconds. - Weight::from_parts(13_960_000, 1526) + // Minimum execution time: 10_513_000 picoseconds. + Weight::from_parts(11_138_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -139,14 +140,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 57_770_000 picoseconds. - Weight::from_parts(61_047_512, 8499) - // Standard Error: 165 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Minimum execution time: 50_864_000 picoseconds. + Weight::from_parts(54_000_280, 8499) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)) } @@ -162,10 +161,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `568` - // Estimated: `2053` - // Minimum execution time: 51_196_000 picoseconds. - Weight::from_parts(52_382_000, 2053) + // Measured: `635` + // Estimated: `2120` + // Minimum execution time: 43_630_000 picoseconds. + Weight::from_parts(44_622_000, 2120) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -185,10 +184,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `686` + // Measured: `753` // Estimated: `4698` - // Minimum execution time: 71_636_000 picoseconds. - Weight::from_parts(73_679_000, 4698) + // Minimum execution time: 62_453_000 picoseconds. + Weight::from_parts(63_882_000, 4698) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -198,8 +197,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 19_182_000 picoseconds. - Weight::from_parts(19_775_000, 3550) + // Minimum execution time: 17_237_000 picoseconds. + Weight::from_parts(17_757_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -209,21 +208,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(21_557_000, 3550) + // Minimum execution time: 18_504_000 picoseconds. + Weight::from_parts(19_273_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `Broker::Regions` (r:1 w:2) + /// Storage: `Broker::Regions` (r:1 w:3) /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 21_190_000 picoseconds. - Weight::from_parts(22_215_000, 3550) + // Minimum execution time: 20_477_000 picoseconds. + Weight::from_parts(21_328_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) @@ -237,8 +236,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `740` // Estimated: `4681` - // Minimum execution time: 34_591_000 picoseconds. - Weight::from_parts(36_227_000, 4681) + // Minimum execution time: 31_815_000 picoseconds. + Weight::from_parts(32_700_000, 4681) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -256,8 +255,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `775` // Estimated: `5996` - // Minimum execution time: 40_346_000 picoseconds. - Weight::from_parts(41_951_000, 5996) + // Minimum execution time: 38_313_000 picoseconds. + Weight::from_parts(38_985_000, 5996) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -272,10 +271,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 75_734_000 picoseconds. - Weight::from_parts(78_168_395, 6196) - // Standard Error: 63_180 - .saturating_add(Weight::from_parts(1_076_259, 0).saturating_mul(m.into())) + // Minimum execution time: 70_170_000 picoseconds. + Weight::from_parts(71_245_388, 6196) + // Standard Error: 54_382 + .saturating_add(Weight::from_parts(1_488_794, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -287,8 +286,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 46_383_000 picoseconds. - Weight::from_parts(47_405_000, 3593) + // Minimum execution time: 43_414_000 picoseconds. + Weight::from_parts(44_475_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -300,8 +299,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `603` // Estimated: `3550` - // Minimum execution time: 30_994_000 picoseconds. - Weight::from_parts(31_979_000, 3550) + // Minimum execution time: 31_327_000 picoseconds. + Weight::from_parts(32_050_000, 3550) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -315,8 +314,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(44_010_000, 3533) + // Minimum execution time: 41_315_000 picoseconds. + Weight::from_parts(42_421_000, 3533) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -330,10 +329,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `830` + // Measured: `995` // Estimated: `3593` - // Minimum execution time: 45_266_000 picoseconds. - Weight::from_parts(48_000_000, 3593) + // Minimum execution time: 49_707_000 picoseconds. + Weight::from_parts(51_516_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -343,10 +342,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: - // Measured: `525` + // Measured: `661` // Estimated: `4698` - // Minimum execution time: 25_365_000 picoseconds. - Weight::from_parts(26_920_000, 4698) + // Minimum execution time: 26_207_000 picoseconds. + Weight::from_parts(27_227_000, 4698) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -355,22 +354,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_519_000 picoseconds. - Weight::from_parts(7_098_698, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(8, 0).saturating_mul(n.into())) + // Minimum execution time: 4_670_000 picoseconds. + Weight::from_parts(5_170_450, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(37, 0).saturating_mul(n.into())) } - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `3563` - // Minimum execution time: 7_608_000 picoseconds. - Weight::from_parts(8_157_815, 3563) - // Standard Error: 26 - .saturating_add(Weight::from_parts(48, 0).saturating_mul(n.into())) + // Measured: `404` + // Estimated: `1487` + // Minimum execution time: 6_916_000 picoseconds. + Weight::from_parts(7_485_053, 1487) + // Standard Error: 23 + .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -386,10 +385,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `905` - // Estimated: `4370` - // Minimum execution time: 59_993_000 picoseconds. - Weight::from_parts(61_752_000, 4370) + // Measured: `972` + // Estimated: `4437` + // Minimum execution time: 50_987_000 picoseconds. + Weight::from_parts(52_303_000, 4437) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -408,10 +407,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 41_863_000 picoseconds. - Weight::from_parts(44_033_031, 8499) - // Standard Error: 116 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(n.into())) + // Minimum execution time: 38_334_000 picoseconds. + Weight::from_parts(40_517_609, 8499) + // Standard Error: 90 + .saturating_add(Weight::from_parts(338, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -423,8 +422,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 9_588_000 picoseconds. - Weight::from_parts(9_925_000, 3493) + // Minimum execution time: 7_850_000 picoseconds. + Weight::from_parts(8_157_000, 3493) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -436,8 +435,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(20_482_000, 4681) + // Minimum execution time: 17_313_000 picoseconds. + Weight::from_parts(17_727_000, 4681) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -445,28 +444,46 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(184_000, 0) + // Minimum execution time: 171_000 picoseconds. + Weight::from_parts(196_000, 0) } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) fn notify_core_count() -> Weight { - T::DbWeight::get().reads_writes(1, 1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_413_000 picoseconds. + Weight::from_parts(2_587_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `699` - // Estimated: `4164` - // Minimum execution time: 19_824_000 picoseconds. - Weight::from_parts(20_983_000, 4164) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 13_121_000 picoseconds. + Weight::from_parts(13_685_000, 4068) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `1526` + // Minimum execution time: 6_847_000 picoseconds. + Weight::from_parts(7_185_000, 1526) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -478,8 +495,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040_000 picoseconds. - Weight::from_parts(3_344_000, 0) + // Minimum execution time: 2_865_000 picoseconds. + Weight::from_parts(3_061_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -488,8 +505,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 21_259_000 picoseconds. - Weight::from_parts(22_110_000, 7496) + // Minimum execution time: 18_431_000 picoseconds. + Weight::from_parts(19_558_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -499,8 +516,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 20_330_000 picoseconds. - Weight::from_parts(20_826_000, 7496) + // Minimum execution time: 17_724_000 picoseconds. + Weight::from_parts(18_688_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -510,8 +527,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 13_411_000 picoseconds. - Weight::from_parts(13_960_000, 1526) + // Minimum execution time: 10_513_000 picoseconds. + Weight::from_parts(11_138_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -530,14 +547,12 @@ impl WeightInfo for () { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 57_770_000 picoseconds. - Weight::from_parts(61_047_512, 8499) - // Standard Error: 165 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Minimum execution time: 50_864_000 picoseconds. + Weight::from_parts(54_000_280, 8499) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(16_u64)) } @@ -553,10 +568,10 @@ impl WeightInfo for () { /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `568` - // Estimated: `2053` - // Minimum execution time: 51_196_000 picoseconds. - Weight::from_parts(52_382_000, 2053) + // Measured: `635` + // Estimated: `2120` + // Minimum execution time: 43_630_000 picoseconds. + Weight::from_parts(44_622_000, 2120) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -576,10 +591,10 @@ impl WeightInfo for () { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `686` + // Measured: `753` // Estimated: `4698` - // Minimum execution time: 71_636_000 picoseconds. - Weight::from_parts(73_679_000, 4698) + // Minimum execution time: 62_453_000 picoseconds. + Weight::from_parts(63_882_000, 4698) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -589,8 +604,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 19_182_000 picoseconds. - Weight::from_parts(19_775_000, 3550) + // Minimum execution time: 17_237_000 picoseconds. + Weight::from_parts(17_757_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -600,21 +615,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(21_557_000, 3550) + // Minimum execution time: 18_504_000 picoseconds. + Weight::from_parts(19_273_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `Broker::Regions` (r:1 w:2) + /// Storage: `Broker::Regions` (r:1 w:3) /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 21_190_000 picoseconds. - Weight::from_parts(22_215_000, 3550) + // Minimum execution time: 20_477_000 picoseconds. + Weight::from_parts(21_328_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) @@ -628,8 +643,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `740` // Estimated: `4681` - // Minimum execution time: 34_591_000 picoseconds. - Weight::from_parts(36_227_000, 4681) + // Minimum execution time: 31_815_000 picoseconds. + Weight::from_parts(32_700_000, 4681) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -647,8 +662,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `775` // Estimated: `5996` - // Minimum execution time: 40_346_000 picoseconds. - Weight::from_parts(41_951_000, 5996) + // Minimum execution time: 38_313_000 picoseconds. + Weight::from_parts(38_985_000, 5996) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -663,10 +678,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 75_734_000 picoseconds. - Weight::from_parts(78_168_395, 6196) - // Standard Error: 63_180 - .saturating_add(Weight::from_parts(1_076_259, 0).saturating_mul(m.into())) + // Minimum execution time: 70_170_000 picoseconds. + Weight::from_parts(71_245_388, 6196) + // Standard Error: 54_382 + .saturating_add(Weight::from_parts(1_488_794, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -678,8 +693,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 46_383_000 picoseconds. - Weight::from_parts(47_405_000, 3593) + // Minimum execution time: 43_414_000 picoseconds. + Weight::from_parts(44_475_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -691,8 +706,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `603` // Estimated: `3550` - // Minimum execution time: 30_994_000 picoseconds. - Weight::from_parts(31_979_000, 3550) + // Minimum execution time: 31_327_000 picoseconds. + Weight::from_parts(32_050_000, 3550) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -706,8 +721,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(44_010_000, 3533) + // Minimum execution time: 41_315_000 picoseconds. + Weight::from_parts(42_421_000, 3533) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -721,10 +736,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `830` + // Measured: `995` // Estimated: `3593` - // Minimum execution time: 45_266_000 picoseconds. - Weight::from_parts(48_000_000, 3593) + // Minimum execution time: 49_707_000 picoseconds. + Weight::from_parts(51_516_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -734,10 +749,10 @@ impl WeightInfo for () { /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: - // Measured: `525` + // Measured: `661` // Estimated: `4698` - // Minimum execution time: 25_365_000 picoseconds. - Weight::from_parts(26_920_000, 4698) + // Minimum execution time: 26_207_000 picoseconds. + Weight::from_parts(27_227_000, 4698) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -746,22 +761,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_519_000 picoseconds. - Weight::from_parts(7_098_698, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(8, 0).saturating_mul(n.into())) + // Minimum execution time: 4_670_000 picoseconds. + Weight::from_parts(5_170_450, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(37, 0).saturating_mul(n.into())) } - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `3563` - // Minimum execution time: 7_608_000 picoseconds. - Weight::from_parts(8_157_815, 3563) - // Standard Error: 26 - .saturating_add(Weight::from_parts(48, 0).saturating_mul(n.into())) + // Measured: `404` + // Estimated: `1487` + // Minimum execution time: 6_916_000 picoseconds. + Weight::from_parts(7_485_053, 1487) + // Standard Error: 23 + .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -777,10 +792,10 @@ impl WeightInfo for () { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `905` - // Estimated: `4370` - // Minimum execution time: 59_993_000 picoseconds. - Weight::from_parts(61_752_000, 4370) + // Measured: `972` + // Estimated: `4437` + // Minimum execution time: 50_987_000 picoseconds. + Weight::from_parts(52_303_000, 4437) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -799,10 +814,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 41_863_000 picoseconds. - Weight::from_parts(44_033_031, 8499) - // Standard Error: 116 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(n.into())) + // Minimum execution time: 38_334_000 picoseconds. + Weight::from_parts(40_517_609, 8499) + // Standard Error: 90 + .saturating_add(Weight::from_parts(338, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -814,8 +829,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 9_588_000 picoseconds. - Weight::from_parts(9_925_000, 3493) + // Minimum execution time: 7_850_000 picoseconds. + Weight::from_parts(8_157_000, 3493) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -827,8 +842,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(20_482_000, 4681) + // Minimum execution time: 17_313_000 picoseconds. + Weight::from_parts(17_727_000, 4681) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -836,28 +851,45 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(184_000, 0) + // Minimum execution time: 171_000 picoseconds. + Weight::from_parts(196_000, 0) } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) fn notify_core_count() -> Weight { - RocksDbWeight::get().reads(1) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_413_000 picoseconds. + Weight::from_parts(2_587_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `699` - // Estimated: `4164` - // Minimum execution time: 19_824_000 picoseconds. - Weight::from_parts(20_983_000, 4164) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 13_121_000 picoseconds. + Weight::from_parts(13_685_000, 4068) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `1526` + // Minimum execution time: 6_847_000 picoseconds. + Weight::from_parts(7_185_000, 1526) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index e2a8d3d1337b..470304ed357e 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -225,6 +225,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = MessageQueueHeapSize; type MaxStale = MessageQueueMaxStale; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); type MessageProcessor = MessageProcessor; type QueueChangeHandler = (); type WeightInfo = (); diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index d22221fe8ee0..39aa9bebc0f5 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -23,7 +23,6 @@ use crate::{ }; use codec::{Decode, Encode}; use frame_support::{ - assert_err, pallet_prelude::Weight, traits::{fungibles::Mutate, Currency}, }; @@ -102,7 +101,7 @@ fn test_xcm_execute() { 0, Weight::MAX, None, - VersionedXcm::V4(message).encode(), + VersionedXcm::V4(message).encode().encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, @@ -146,7 +145,7 @@ fn test_xcm_execute_incomplete() { 0, Weight::MAX, None, - VersionedXcm::V4(message).encode(), + VersionedXcm::V4(message).encode().encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, @@ -160,37 +159,6 @@ fn test_xcm_execute_incomplete() { }); } -#[test] -fn test_xcm_execute_filtered_call() { - MockNet::reset(); - - let contract_addr = instantiate_test_contract("xcm_execute"); - - ParaA::execute_with(|| { - // `remark` should be rejected, as it is not allowed by our CallFilter. - let call = parachain::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - let message: Xcm = Xcm(vec![Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: Weight::MAX, - call: call.encode().into(), - }]); - - let result = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - VersionedXcm::V4(message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - assert_err!(result.result, frame_system::Error::::CallFiltered); - }); -} - #[test] fn test_xcm_execute_reentrant_call() { MockNet::reset(); @@ -222,7 +190,7 @@ fn test_xcm_execute_reentrant_call() { 0, Weight::MAX, None, - VersionedXcm::V4(message).encode(), + VersionedXcm::V4(message).encode().encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, @@ -258,7 +226,7 @@ fn test_xcm_send() { 0, Weight::MAX, None, - (dest, message).encode(), + (dest, message.encode()).encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 6433d4eecdc1..e14a4b8bcb87 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -298,6 +298,9 @@ pub mod pallet { /// Therefore please make sure to be restrictive about which dispatchables are allowed /// in order to not introduce a new DoS vector like memory allocation patterns that can /// be exploited to drive the runtime into a panic. + /// + /// This filter does not apply to XCM transact calls. To impose restrictions on XCM transact + /// calls, you must configure them separately within the XCM pallet itself. type CallFilter: Contains<::RuntimeCall>; /// Used to answer contracts' queries regarding the current weight price. This is **not** diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 160dfa0d2f36..28a08ab0224d 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -25,12 +25,8 @@ use crate::{ }; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{ - dispatch::DispatchInfo, - ensure, - pallet_prelude::{DispatchResult, DispatchResultWithPostInfo}, - parameter_types, - traits::Get, - weights::Weight, + dispatch::DispatchInfo, ensure, pallet_prelude::DispatchResultWithPostInfo, parameter_types, + traits::Get, weights::Weight, }; use pallet_contracts_proc_macro::define_env; use pallet_contracts_uapi::{CallFlags, ReturnFlags}; @@ -41,9 +37,6 @@ use sp_runtime::{ }; use sp_std::{fmt, prelude::*}; use wasmi::{core::HostError, errors::LinkerError, Linker, Memory, Store}; -use xcm::VersionedXcm; - -type CallOf = ::RuntimeCall; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; @@ -378,29 +371,6 @@ fn already_charged(_: u32) -> Option { None } -/// Ensure that the XCM program is executable, by checking that it does not contain any [`Transact`] -/// instruction with a call that is not allowed by the CallFilter. -fn ensure_executable(message: &VersionedXcm>) -> DispatchResult { - use frame_support::traits::Contains; - use xcm::prelude::{Transact, Xcm}; - - let mut message: Xcm> = - message.clone().try_into().map_err(|_| Error::::XCMDecodeFailed)?; - - message.iter_mut().try_for_each(|inst| -> DispatchResult { - let Transact { ref mut call, .. } = inst else { return Ok(()) }; - let call = call.ensure_decoded().map_err(|_| Error::::XCMDecodeFailed)?; - - if !::CallFilter::contains(call) { - return Err(frame_system::Error::::CallFiltered.into()) - } - - Ok(()) - })?; - - Ok(()) -} - /// Can only be used for one call. pub struct Runtime<'a, E: Ext + 'a> { ext: &'a mut E, @@ -2112,16 +2082,13 @@ pub mod env { msg_len: u32, ) -> Result { use frame_support::dispatch::DispatchInfo; - use xcm::VersionedXcm; use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo}; ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; - let message: VersionedXcm> = - ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; - ensure_executable::(&message)?; + let message = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; let execute_weight = - <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); + <::Xcm as ExecuteController<_, _>>::WeightInfo::execute_blob(); let weight = ctx.ext.gas_meter().gas_left().max(execute_weight); let dispatch_info = DispatchInfo { weight, ..Default::default() }; @@ -2130,9 +2097,9 @@ pub mod env { RuntimeCosts::CallXcmExecute, |ctx| { let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); - let weight_used = <::Xcm>::execute( + let weight_used = <::Xcm>::execute_blob( origin, - Box::new(message), + message, weight.saturating_sub(execute_weight), )?; @@ -2152,19 +2119,18 @@ pub mod env { msg_len: u32, output_ptr: u32, ) -> Result { - use xcm::{VersionedLocation, VersionedXcm}; + use xcm::VersionedLocation; use xcm_builder::{SendController, SendControllerWeightInfo}; ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let dest: VersionedLocation = ctx.read_sandbox_memory_as(memory, dest_ptr)?; - let message: VersionedXcm<()> = - ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; - let weight = <::Xcm as SendController<_>>::WeightInfo::send(); + let message = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; + let weight = <::Xcm as SendController<_>>::WeightInfo::send_blob(); ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?; let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); - match <::Xcm>::send(origin, dest.into(), message.into()) { + match <::Xcm>::send_blob(origin, dest.into(), message) { Ok(message_id) => { ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?; Ok(ReturnErrorCode::Success) diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index 04f58895ab4f..459cb59bead9 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -790,7 +790,7 @@ pub trait HostFn { /// /// # Parameters /// - /// - `dest`: The XCM destination, should be decodable as [VersionedMultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedMultiLocation.html), + /// - `dest`: The XCM destination, should be decodable as [MultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), /// traps otherwise. /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), /// traps otherwise. diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 31a79577d1f5..11577cd35262 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -586,10 +586,8 @@ pub mod pallet { type EstimateCallFee: EstimateCallFee, BalanceOf>; /// Duration of the unsigned phase. - #[pallet::constant] type UnsignedPhase: Get>; /// Duration of the signed phase. - #[pallet::constant] type SignedPhase: Get>; /// The minimum amount of improvement to the solution score that defines a solution as diff --git a/substrate/frame/message-queue/src/integration_test.rs b/substrate/frame/message-queue/src/integration_test.rs index 26a330cc88e8..14b8d2217eb2 100644 --- a/substrate/frame/message-queue/src/integration_test.rs +++ b/substrate/frame/message-queue/src/integration_test.rs @@ -73,6 +73,7 @@ impl Config for Test { type HeapSize = HeapSize; type MaxStale = MaxStale; type ServiceWeight = ServiceWeight; + type IdleMaxServiceWeight = (); } /// Simulates heavy usage by enqueueing and processing large amounts of messages. diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 93cd760eeb96..ec85c785f79e 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -525,12 +525,21 @@ pub mod pallet { type MaxStale: Get; /// The amount of weight (if any) which should be provided to the message queue for - /// servicing enqueued items. + /// servicing enqueued items `on_initialize`. /// /// This may be legitimately `None` in the case that you will call - /// `ServiceQueues::service_queues` manually. + /// `ServiceQueues::service_queues` manually or set [`Self::IdleMaxServiceWeight`] to have + /// it run in `on_idle`. #[pallet::constant] type ServiceWeight: Get>; + + /// The maximum amount of weight (if any) to be used from remaining weight `on_idle` which + /// should be provided to the message queue for servicing enqueued items `on_idle`. + /// Useful for parachains to process messages at the same block they are received. + /// + /// If `None`, it will not call `ServiceQueues::service_queues` in `on_idle`. + #[pallet::constant] + type IdleMaxServiceWeight: Get>; } #[pallet::event] @@ -643,6 +652,15 @@ pub mod pallet { } } + fn on_idle(_n: BlockNumberFor, remaining_weight: Weight) -> Weight { + if let Some(weight_limit) = T::IdleMaxServiceWeight::get() { + // Make use of the remaining weight to process enqueued messages. + Self::service_queues(weight_limit.min(remaining_weight)) + } else { + Weight::zero() + } + } + #[cfg(feature = "try-runtime")] fn try_state(_: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { Self::do_try_state() diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index f22f318b8ef1..1281de6b0a66 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -56,6 +56,7 @@ impl Config for Test { type HeapSize = HeapSize; type MaxStale = MaxStale; type ServiceWeight = ServiceWeight; + type IdleMaxServiceWeight = ServiceWeight; } /// Mocked `WeightInfo` impl with allows to set the weight per call. diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index 1f6e7777f012..d6788847d571 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -1838,3 +1838,45 @@ fn with_service_mutex_works() { with_service_mutex(|| called = 3).unwrap(); assert_eq!(called, 3); } + +#[test] +fn process_enqueued_on_idle() { + use MessageOrigin::*; + build_and_execute::(|| { + // Some messages enqueued on previous block. + MessageQueue::enqueue_messages(vec![msg("a"), msg("ab"), msg("abc")].into_iter(), Here); + assert_eq!(BookStateFor::::iter().count(), 1); + + // Process enqueued messages from previous block. + Pallet::::on_initialize(1); + assert_eq!( + MessagesProcessed::take(), + vec![(b"a".to_vec(), Here), (b"ab".to_vec(), Here), (b"abc".to_vec(), Here),] + ); + + MessageQueue::enqueue_messages(vec![msg("x"), msg("xy"), msg("xyz")].into_iter(), There); + assert_eq!(BookStateFor::::iter().count(), 2); + + // Enough weight to process on idle. + Pallet::::on_idle(1, Weight::from_parts(100, 100)); + assert_eq!( + MessagesProcessed::take(), + vec![(b"x".to_vec(), There), (b"xy".to_vec(), There), (b"xyz".to_vec(), There)] + ); + }) +} + +#[test] +fn process_enqueued_on_idle_requires_enough_weight() { + use MessageOrigin::*; + build_and_execute::(|| { + Pallet::::on_initialize(1); + + MessageQueue::enqueue_messages(vec![msg("x"), msg("xy"), msg("xyz")].into_iter(), There); + assert_eq!(BookStateFor::::iter().count(), 1); + + // Not enough weight to process on idle. + Pallet::::on_idle(1, Weight::from_parts(0, 0)); + assert_eq!(MessagesProcessed::take(), vec![]); + }) +} diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs index e616056c3022..fbe27e1a4784 100644 --- a/substrate/frame/referenda/src/lib.rs +++ b/substrate/frame/referenda/src/lib.rs @@ -424,6 +424,8 @@ pub mod pallet { BadStatus, /// The preimage does not exist. PreimageNotExist, + /// The preimage is stored with a different length than the one provided. + PreimageStoredWithDifferentLength, } #[pallet::hooks] @@ -462,6 +464,16 @@ pub mod pallet { let proposal_origin = *proposal_origin; let who = T::SubmitOrigin::ensure_origin(origin, &proposal_origin)?; + // If the pre-image is already stored, ensure that it has the same length as given in + // `proposal`. + if let (Some(preimage_len), Some(proposal_len)) = + (proposal.lookup_hash().and_then(|h| T::Preimages::len(&h)), proposal.lookup_len()) + { + if preimage_len != proposal_len { + return Err(Error::::PreimageStoredWithDifferentLength.into()) + } + } + let track = T::Tracks::track_for(&proposal_origin).map_err(|_| Error::::NoTrack)?; let submission_deposit = Self::take_deposit(who, T::SubmissionDeposit::get())?; diff --git a/substrate/frame/referenda/src/tests.rs b/substrate/frame/referenda/src/tests.rs index 8f51136de0bf..52251fcbdbee 100644 --- a/substrate/frame/referenda/src/tests.rs +++ b/substrate/frame/referenda/src/tests.rs @@ -666,3 +666,19 @@ fn clear_metadata_works() { })); }); } + +#[test] +fn detects_incorrect_len() { + ExtBuilder::default().build_and_execute(|| { + let hash = note_preimage(1); + assert_noop!( + Referenda::submit( + RuntimeOrigin::signed(1), + Box::new(RawOrigin::Root.into()), + frame_support::traits::Bounded::Lookup { hash, len: 3 }, + DispatchTime::At(1), + ), + Error::::PreimageStoredWithDifferentLength + ); + }); +} diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 62417b8d2cc2..d19a1e0001dd 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -1267,6 +1267,17 @@ impl Pallet { id: task.maybe_id, }); + // It was not available when we needed it, so we don't need to have requested it + // anymore. + T::Preimages::drop(&task.call); + + // We don't know why `peek` failed, thus we most account here for the "full weight". + let _ = weight.try_consume(T::WeightInfo::service_task( + task.call.lookup_len().map(|x| x as usize), + task.maybe_id.is_some(), + task.maybe_periodic.is_some(), + )); + return Err((Unavailable, Some(task))) }, }; @@ -1435,6 +1446,7 @@ impl Pallet { } } +#[allow(deprecated)] impl schedule::v2::Anon, ::RuntimeCall, T::PalletsOrigin> for Pallet { @@ -1469,6 +1481,8 @@ impl schedule::v2::Anon, ::RuntimeCall } } +// TODO: migrate `schedule::v2::Anon` to `v3` +#[allow(deprecated)] impl schedule::v2::Named, ::RuntimeCall, T::PalletsOrigin> for Pallet { diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index bb02320ad751..440355336396 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -3008,6 +3008,8 @@ fn unavailable_call_is_detected() { // Ensure the preimage isn't available assert!(!Preimage::have(&bound)); + // But we have requested it + assert!(Preimage::is_requested(&hash)); // Executes in block 4. run_to_block(4); @@ -3016,5 +3018,7 @@ fn unavailable_call_is_detected() { System::events().last().unwrap().event, crate::Event::CallUnavailable { task: (4, 0), id: Some(name) }.into() ); + // It should not be requested anymore. + assert!(!Preimage::is_requested(&hash)); }); } diff --git a/substrate/frame/staking/Cargo.toml b/substrate/frame/staking/Cargo.toml index d2a46146931b..15c4bf9e290e 100644 --- a/substrate/frame/staking/Cargo.toml +++ b/substrate/frame/staking/Cargo.toml @@ -40,10 +40,10 @@ frame-benchmarking = { path = "../benchmarking", default-features = false, optio rand_chacha = { version = "0.2", default-features = false, optional = true } [dev-dependencies] +pallet-balances = { path = "../balances" } sp-tracing = { path = "../../primitives/tracing" } sp-core = { path = "../../primitives/core" } sp-npos-elections = { path = "../../primitives/npos-elections" } -pallet-balances = { path = "../balances" } pallet-timestamp = { path = "../timestamp" } pallet-staking-reward-curve = { path = "reward-curve" } pallet-bags-list = { path = "../bags-list" } diff --git a/substrate/frame/staking/src/benchmarking.rs b/substrate/frame/staking/src/benchmarking.rs index a83060873973..0b67cd460395 100644 --- a/substrate/frame/staking/src/benchmarking.rs +++ b/substrate/frame/staking/src/benchmarking.rs @@ -953,6 +953,15 @@ benchmarks! { assert_eq!(MinCommission::::get(), Perbill::from_percent(100)); } + restore_ledger { + let (stash, controller) = create_stash_controller::(0, 100, RewardDestination::Staked)?; + // corrupt ledger. + Ledger::::remove(controller); + }: _(RawOrigin::Root, stash.clone(), None, None, None) + verify { + assert_eq!(Staking::::inspect_bond_state(&stash), Ok(LedgerIntegrityState::Ok)); + } + impl_benchmark_test_suite!( Staking, crate::mock::ExtBuilder::default().has_stakers(true), diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index b91b1bd28f35..9a4386086aa6 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -506,6 +506,20 @@ pub struct StakingLedger { controller: Option, } +/// State of a ledger with regards with its data and metadata integrity. +#[derive(PartialEq, Debug)] +enum LedgerIntegrityState { + /// Ledger, bond and corresponding staking lock is OK. + Ok, + /// Ledger and/or bond is corrupted. This means that the bond has a ledger with a different + /// stash than the bonded stash. + Corrupted, + /// Ledger was corrupted and it has been killed. + CorruptedKilled, + /// Ledger and bond are OK, however the ledger's stash lock is out of sync. + LockCorrupted, +} + impl StakingLedger { /// Remove entries from `unlocking` that are sufficiently old and optionally upto a given limit. /// Reduce the total by the unlocked amount. diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 58a80994a62c..ae55b2afabbb 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -25,8 +25,8 @@ use frame_election_provider_support::{ use frame_support::{ assert_ok, derive_impl, ord_parameter_types, parameter_types, traits::{ - ConstU64, Currency, EitherOfDiverse, FindAuthor, Get, Hooks, Imbalance, OnUnbalanced, - OneSessionHandler, + ConstU64, Currency, EitherOfDiverse, FindAuthor, Get, Hooks, Imbalance, LockableCurrency, + OnUnbalanced, OneSessionHandler, WithdrawReasons, }, weights::constants::RocksDbWeight, }; @@ -787,55 +787,86 @@ pub(crate) fn bond_controller_stash(controller: AccountId, stash: AccountId) -> Ok(()) } +// simulates `set_controller` without corrupted ledger checks for testing purposes. +pub(crate) fn set_controller_no_checks(stash: &AccountId) { + let controller = Bonded::::get(stash).expect("testing stash should be bonded"); + let ledger = Ledger::::get(&controller).expect("testing ledger should exist"); + + Ledger::::remove(&controller); + Ledger::::insert(stash, ledger); + Bonded::::insert(stash, stash); +} + +// simulates `bond_extra` without corrupted ledger checks for testing purposes. +pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) { + let controller = Bonded::::get(stash).expect("bond must exist to bond_extra"); + let mut ledger = Ledger::::get(&controller).expect("ledger must exist to bond_extra"); + + let new_total = ledger.total + amount; + Balances::set_lock(crate::STAKING_ID, stash, new_total, WithdrawReasons::all()); + ledger.total = new_total; + ledger.active = new_total; + Ledger::::insert(controller, ledger); +} + pub(crate) fn setup_double_bonded_ledgers() { - assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 10, RewardDestination::Staked)); - assert_ok!(Staking::bond(RuntimeOrigin::signed(2), 20, RewardDestination::Staked)); - assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 20, RewardDestination::Staked)); + let init_ledgers = Ledger::::iter().count(); + + let _ = Balances::make_free_balance_be(&333, 2000); + let _ = Balances::make_free_balance_be(&444, 2000); + let _ = Balances::make_free_balance_be(&555, 2000); + let _ = Balances::make_free_balance_be(&777, 2000); + + assert_ok!(Staking::bond(RuntimeOrigin::signed(333), 10, RewardDestination::Staked)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(444), 20, RewardDestination::Staked)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(555), 20, RewardDestination::Staked)); // not relevant to the test case, but ensures try-runtime checks pass. - [1, 2, 3] + [333, 444, 555] .iter() .for_each(|s| Payee::::insert(s, RewardDestination::Staked)); // we want to test the case where a controller can also be a stash of another ledger. // for that, we change the controller/stash bonding so that: - // * 2 becomes controller of 1. - // * 3 becomes controller of 2. - // * 4 becomes controller of 3. - let ledger_1 = Ledger::::get(1).unwrap(); - let ledger_2 = Ledger::::get(2).unwrap(); - let ledger_3 = Ledger::::get(3).unwrap(); - - // 4 becomes controller of 3. - Bonded::::mutate(3, |controller| *controller = Some(4)); - Ledger::::insert(4, ledger_3); - - // 3 becomes controller of 2. - Bonded::::mutate(2, |controller| *controller = Some(3)); - Ledger::::insert(3, ledger_2); - - // 2 becomes controller of 1 - Bonded::::mutate(1, |controller| *controller = Some(2)); - Ledger::::insert(2, ledger_1); - // 1 is not controller anymore. - Ledger::::remove(1); + // * 444 becomes controller of 333. + // * 555 becomes controller of 444. + // * 777 becomes controller of 555. + let ledger_333 = Ledger::::get(333).unwrap(); + let ledger_444 = Ledger::::get(444).unwrap(); + let ledger_555 = Ledger::::get(555).unwrap(); + + // 777 becomes controller of 555. + Bonded::::mutate(555, |controller| *controller = Some(777)); + Ledger::::insert(777, ledger_555); + + // 555 becomes controller of 444. + Bonded::::mutate(444, |controller| *controller = Some(555)); + Ledger::::insert(555, ledger_444); + + // 444 becomes controller of 333. + Bonded::::mutate(333, |controller| *controller = Some(444)); + Ledger::::insert(444, ledger_333); + + // 333 is not controller anymore. + Ledger::::remove(333); // checks. now we have: - // * 3 ledgers - assert_eq!(Ledger::::iter().count(), 3); - // * stash 1 has controller 2. - assert_eq!(Bonded::::get(1), Some(2)); - assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(1)), Some(2)); - assert_eq!(Ledger::::get(2).unwrap().stash, 1); - - // * stash 2 has controller 3. - assert_eq!(Bonded::::get(2), Some(3)); - assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(2)), Some(3)); - assert_eq!(Ledger::::get(3).unwrap().stash, 2); - - // * stash 3 has controller 4. - assert_eq!(Bonded::::get(3), Some(4)); - assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(3)), Some(4)); - assert_eq!(Ledger::::get(4).unwrap().stash, 3); + // * +3 ledgers + assert_eq!(Ledger::::iter().count(), 3 + init_ledgers); + + // * stash 333 has controller 444. + assert_eq!(Bonded::::get(333), Some(444)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(333)), Some(444)); + assert_eq!(Ledger::::get(444).unwrap().stash, 333); + + // * stash 444 has controller 555. + assert_eq!(Bonded::::get(444), Some(555)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(444)), Some(555)); + assert_eq!(Ledger::::get(555).unwrap().stash, 444); + + // * stash 555 has controller 777. + assert_eq!(Bonded::::get(555), Some(777)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(555)), Some(777)); + assert_eq!(Ledger::::get(777).unwrap().stash, 555); } #[macro_export] diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 5c49270449d3..887af37ae638 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -27,8 +27,8 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Currency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, - LockableCurrency, OnUnbalanced, TryCollect, UnixTime, + Currency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, + InspectLockableCurrency, Len, LockableCurrency, OnUnbalanced, TryCollect, UnixTime, }, weights::Weight, }; @@ -51,8 +51,8 @@ use sp_std::prelude::*; use crate::{ election_size_tracker::StaticTracker, log, slashing, weights::WeightInfo, ActiveEraInfo, BalanceOf, EraInfo, EraPayout, Exposure, ExposureOf, Forcing, IndividualExposure, - MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota, PositiveImbalanceOf, - RewardDestination, SessionInterface, StakingLedger, ValidatorPrefs, + LedgerIntegrityState, MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota, + PositiveImbalanceOf, RewardDestination, SessionInterface, StakingLedger, ValidatorPrefs, }; use super::pallet::*; @@ -86,6 +86,38 @@ impl Pallet { StakingLedger::::paired_account(Stash(stash.clone())) } + /// Inspects and returns the corruption state of a ledger and bond, if any. + /// + /// Note: all operations in this method access directly the `Bonded` and `Ledger` storage maps + /// instead of using the [`StakingLedger`] API since the bond and/or ledger may be corrupted. + pub(crate) fn inspect_bond_state( + stash: &T::AccountId, + ) -> Result> { + let lock = T::Currency::balance_locked(crate::STAKING_ID, &stash); + + let controller = >::get(stash).ok_or_else(|| { + if lock == Zero::zero() { + Error::::NotStash + } else { + Error::::BadState + } + })?; + + match Ledger::::get(controller) { + Some(ledger) => + if ledger.stash != *stash { + Ok(LedgerIntegrityState::Corrupted) + } else { + if lock != ledger.total { + Ok(LedgerIntegrityState::LockCorrupted) + } else { + Ok(LedgerIntegrityState::Ok) + } + }, + None => Ok(LedgerIntegrityState::CorruptedKilled), + } + } + /// The total balance that can be slashed from a stash account as of right now. pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf { // Weight note: consider making the stake accessible through stash. @@ -1900,12 +1932,12 @@ impl Pallet { "VoterList contains non-staker" ); + Self::check_ledgers()?; Self::check_bonded_consistency()?; Self::check_payees()?; Self::check_nominators()?; Self::check_exposures()?; Self::check_paged_exposures()?; - Self::check_ledgers()?; Self::check_count() } @@ -1914,6 +1946,7 @@ impl Pallet { /// * A bonded (stash, controller) pair should have only one associated ledger. I.e. if the /// ledger is bonded by stash, the controller account must not bond a different ledger. /// * A bonded (stash, controller) pair must have an associated ledger. + /// /// NOTE: these checks result in warnings only. Once /// is resolved, turn warns into check /// failures. @@ -2008,19 +2041,18 @@ impl Pallet { } /// Invariants: - /// * `ledger.controller` is not stored in the storage (but populated at retrieval). /// * Stake consistency: ledger.total == ledger.active + sum(ledger.unlocking). - /// * The controller keying the ledger and the ledger stash matches the state of the `Bonded` - /// storage. + /// * The ledger's controller and stash matches the associated `Bonded` tuple. + /// * Staking locked funds for every bonded stash should be the same as its ledger's total. + /// * Staking ledger and bond are not corrupted. fn check_ledgers() -> Result<(), TryRuntimeError> { Bonded::::iter() .map(|(stash, ctrl)| { - // `ledger.controller` is never stored in raw storage. - let raw = Ledger::::get(stash).unwrap_or_else(|| { - Ledger::::get(ctrl.clone()) - .expect("try_check: bonded stash/ctrl does not have an associated ledger") - }); - ensure!(raw.controller.is_none(), "raw storage controller should be None"); + // ensure locks consistency. + ensure!( + Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok), + "bond, ledger and/or staking lock inconsistent for a bonded stash." + ); // ensure ledger consistency. Self::ensure_ledger_consistent(ctrl) diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index eab0a773f520..6b0df8ffcb91 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -25,7 +25,7 @@ use frame_support::{ pallet_prelude::*, traits::{ Currency, Defensive, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get, - LockableCurrency, OnUnbalanced, UnixTime, + InspectLockableCurrency, LockableCurrency, OnUnbalanced, UnixTime, WithdrawReasons, }, weights::Weight, BoundedVec, @@ -49,9 +49,9 @@ pub use impls::*; use crate::{ slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout, - EraRewardPoints, Exposure, ExposurePage, Forcing, MaxNominationsOf, NegativeImbalanceOf, - Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination, SessionInterface, - StakingLedger, UnappliedSlash, UnlockChunk, ValidatorPrefs, + EraRewardPoints, Exposure, ExposurePage, Forcing, LedgerIntegrityState, MaxNominationsOf, + NegativeImbalanceOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination, + SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk, ValidatorPrefs, }; // The speculative number of spans are used as an input of the weight annotation of @@ -89,10 +89,10 @@ pub mod pallet { pub trait Config: frame_system::Config { /// The staking balance. type Currency: LockableCurrency< - Self::AccountId, - Moment = BlockNumberFor, - Balance = Self::CurrencyBalance, - >; + Self::AccountId, + Moment = BlockNumberFor, + Balance = Self::CurrencyBalance, + > + InspectLockableCurrency; /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to /// `From`. type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned @@ -804,6 +804,7 @@ pub mod pallet { } #[pallet::error] + #[derive(PartialEq)] pub enum Error { /// Not a controller account. NotController, @@ -867,6 +868,8 @@ pub mod pallet { RewardDestinationRestricted, /// Not enough funds available to withdraw NotEnoughFunds, + /// Cannot reset a ledger. + CannotRestoreLedger, } #[pallet::hooks] @@ -1992,6 +1995,108 @@ pub mod pallet { Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into()) } + + /// Restores the state of a ledger which is in an inconsistent state. + /// + /// The requirements to restore a ledger are the following: + /// * The stash is bonded; or + /// * The stash is not bonded but it has a staking lock left behind; or + /// * If the stash has an associated ledger and its state is inconsistent; or + /// * If the ledger is not corrupted *but* its staking lock is out of sync. + /// + /// The `maybe_*` input parameters will overwrite the corresponding data and metadata of the + /// ledger associated with the stash. If the input parameters are not set, the ledger will + /// be reset values from on-chain state. + #[pallet::call_index(29)] + #[pallet::weight(T::WeightInfo::restore_ledger())] + pub fn restore_ledger( + origin: OriginFor, + stash: T::AccountId, + maybe_controller: Option, + maybe_total: Option>, + maybe_unlocking: Option>, T::MaxUnlockingChunks>>, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + let current_lock = T::Currency::balance_locked(crate::STAKING_ID, &stash); + let stash_balance = T::Currency::free_balance(&stash); + + let (new_controller, new_total) = match Self::inspect_bond_state(&stash) { + Ok(LedgerIntegrityState::Corrupted) => { + let new_controller = maybe_controller.unwrap_or(stash.clone()); + + let new_total = if let Some(total) = maybe_total { + let new_total = total.min(stash_balance); + // enforce lock == ledger.amount. + T::Currency::set_lock( + crate::STAKING_ID, + &stash, + new_total, + WithdrawReasons::all(), + ); + new_total + } else { + current_lock + }; + + Ok((new_controller, new_total)) + }, + Ok(LedgerIntegrityState::CorruptedKilled) => { + if current_lock == Zero::zero() { + // this case needs to restore both lock and ledger, so the new total needs + // to be given by the called since there's no way to restore the total + // on-chain. + ensure!(maybe_total.is_some(), Error::::CannotRestoreLedger); + Ok(( + stash.clone(), + maybe_total.expect("total exists as per the check above; qed."), + )) + } else { + Ok((stash.clone(), current_lock)) + } + }, + Ok(LedgerIntegrityState::LockCorrupted) => { + // ledger is not corrupted but its locks are out of sync. In this case, we need + // to enforce a new ledger.total and staking lock for this stash. + let new_total = + maybe_total.ok_or(Error::::CannotRestoreLedger)?.min(stash_balance); + T::Currency::set_lock( + crate::STAKING_ID, + &stash, + new_total, + WithdrawReasons::all(), + ); + + Ok((stash.clone(), new_total)) + }, + Err(Error::::BadState) => { + // the stash and ledger do not exist but lock is lingering. + T::Currency::remove_lock(crate::STAKING_ID, &stash); + ensure!( + Self::inspect_bond_state(&stash) == Err(Error::::NotStash), + Error::::BadState + ); + + return Ok(()); + }, + Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::::CannotRestoreLedger), + }?; + + // re-bond stash and controller tuple. + Bonded::::insert(&stash, &new_controller); + + // resoter ledger state. + let mut ledger = StakingLedger::::new(stash.clone(), new_total); + ledger.controller = Some(new_controller); + ledger.unlocking = maybe_unlocking.unwrap_or_default(); + ledger.update()?; + + ensure!( + Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok), + Error::::BadState + ); + Ok(()) + } } } diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index ef156e195527..a5c9abe2f176 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -6933,40 +6933,43 @@ mod ledger { setup_double_bonded_ledgers(); // Case 1: double bonded but not corrupted: - // stash 2 has controller 3: - assert_eq!(Bonded::::get(2), Some(3)); - assert_eq!(Ledger::::get(3).unwrap().stash, 2); + // stash 444 has controller 555: + assert_eq!(Bonded::::get(444), Some(555)); + assert_eq!(Ledger::::get(555).unwrap().stash, 444); - // stash 2 is also a controller of 1: - assert_eq!(Bonded::::get(1), Some(2)); - assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(1)), Some(2)); - assert_eq!(Ledger::::get(2).unwrap().stash, 1); + // stash 444 is also a controller of 333: + assert_eq!(Bonded::::get(333), Some(444)); + assert_eq!( + StakingLedger::::paired_account(StakingAccount::Stash(333)), + Some(444) + ); + assert_eq!(Ledger::::get(444).unwrap().stash, 333); - // although 2 is double bonded (it is a controller and a stash of different ledgers), + // although 444 is double bonded (it is a controller and a stash of different ledgers), // we can safely retrieve the ledger and mutate it since the correct ledger is // returned. - let ledger_result = StakingLedger::::get(StakingAccount::Stash(2)); - assert_eq!(ledger_result.unwrap().stash, 2); // correct ledger. + let ledger_result = StakingLedger::::get(StakingAccount::Stash(444)); + assert_eq!(ledger_result.unwrap().stash, 444); // correct ledger. - let ledger_result = StakingLedger::::get(StakingAccount::Controller(2)); - assert_eq!(ledger_result.unwrap().stash, 1); // correct ledger. + let ledger_result = StakingLedger::::get(StakingAccount::Controller(444)); + assert_eq!(ledger_result.unwrap().stash, 333); // correct ledger. - // fetching ledger 1 by its stash works. - let ledger_result = StakingLedger::::get(StakingAccount::Stash(1)); - assert_eq!(ledger_result.unwrap().stash, 1); + // fetching ledger 333 by its stash works. + let ledger_result = StakingLedger::::get(StakingAccount::Stash(333)); + assert_eq!(ledger_result.unwrap().stash, 333); // Case 2: corrupted ledger bonding. // in this case, we simulate what happens when fetching a ledger by stash returns a // ledger with a different stash. when this happens, we return an error instead of the // ledger to prevent ledger mutations. - let mut ledger = Ledger::::get(2).unwrap(); - assert_eq!(ledger.stash, 1); - ledger.stash = 2; - Ledger::::insert(2, ledger); + let mut ledger = Ledger::::get(444).unwrap(); + assert_eq!(ledger.stash, 333); + ledger.stash = 444; + Ledger::::insert(444, ledger); // now, we are prevented from fetching the ledger by stash from 1. It's associated // controller (2) is now bonding a ledger with a different stash (2, not 1). - assert!(StakingLedger::::get(StakingAccount::Stash(1)).is_err()); + assert!(StakingLedger::::get(StakingAccount::Stash(333)).is_err()); }) } @@ -7069,7 +7072,7 @@ mod ledger { #[test] fn deprecate_controller_batch_works_full_weight() { - ExtBuilder::default().build_and_execute(|| { + ExtBuilder::default().try_state(false).build_and_execute(|| { // Given: let start = 1001; @@ -7253,7 +7256,7 @@ mod ledger { let bounded_controllers: BoundedVec< _, ::MaxControllersInDeprecationBatch, - > = BoundedVec::try_from(vec![1, 2, 3, 4]).unwrap(); + > = BoundedVec::try_from(vec![333, 444, 555, 777]).unwrap(); assert_ok!(Staking::deprecate_controller_batch( RuntimeOrigin::root(), @@ -7276,7 +7279,7 @@ mod ledger { let bounded_controllers: BoundedVec< _, ::MaxControllersInDeprecationBatch, - > = BoundedVec::try_from(vec![4, 3, 2, 1]).unwrap(); + > = BoundedVec::try_from(vec![777, 555, 444, 333]).unwrap(); assert_ok!(Staking::deprecate_controller_batch( RuntimeOrigin::root(), @@ -7296,9 +7299,9 @@ mod ledger { setup_double_bonded_ledgers(); // in this case, setting controller works due to the ordering of the calls. - assert_ok!(Staking::set_controller(RuntimeOrigin::signed(1))); - assert_ok!(Staking::set_controller(RuntimeOrigin::signed(2))); - assert_ok!(Staking::set_controller(RuntimeOrigin::signed(3))); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(333))); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(444))); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(555))); }) } @@ -7307,17 +7310,400 @@ mod ledger { ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| { setup_double_bonded_ledgers(); - // setting the controller of ledger associated with stash 3 fails since its stash is a + // setting the controller of ledger associated with stash 555 fails since its stash is a // controller of another ledger. assert_noop!( - Staking::set_controller(RuntimeOrigin::signed(3)), + Staking::set_controller(RuntimeOrigin::signed(555)), Error::::BadState ); assert_noop!( - Staking::set_controller(RuntimeOrigin::signed(2)), + Staking::set_controller(RuntimeOrigin::signed(444)), Error::::BadState ); - assert_ok!(Staking::set_controller(RuntimeOrigin::signed(1))); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(333))); + }) + } +} + +mod ledger_recovery { + use super::*; + use frame_support::traits::InspectLockableCurrency; + + #[test] + fn inspect_recovery_ledger_simple_works() { + ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // non corrupted ledger. + assert_eq!(Staking::inspect_bond_state(&11).unwrap(), LedgerIntegrityState::Ok); + + // non bonded stash. + assert!(Bonded::::get(&1111).is_none()); + assert!(Staking::inspect_bond_state(&1111).is_err()); + + // double bonded but not corrupted. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + }) + } + + #[test] + fn inspect_recovery_ledger_corupted_killed_works() { + ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333); + + // get into corrupted and killed ledger state by killing a corrupted ledger: + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + // kill(333) + // (444, 444) -> corrupted and None. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + + // now try-state fails. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // 333 is corrupted since it's controller is linking 444 ledger. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted); + // 444 however is OK. + assert_eq!(Staking::inspect_bond_state(&444).unwrap(), LedgerIntegrityState::Ok); + + // kill the corrupted ledger that is associated with stash 333. + assert_ok!(StakingLedger::::kill(&333)); + + // 333 bond is no more but it returns `BadState` because the lock on this stash is + // still set (see checks below). + assert_eq!(Staking::inspect_bond_state(&333), Err(Error::::BadState)); + // now the *other* ledger associated with 444 has been corrupted and killed (None). + assert_eq!( + Staking::inspect_bond_state(&444), + Ok(LedgerIntegrityState::CorruptedKilled) + ); + + // side effects on 333 - ledger, bonded, payee, lock should be completely empty. + // however, 333 lock remains. + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); // NOK + assert!(Bonded::::get(&333).is_none()); // OK + assert!(Payee::::get(&333).is_none()); // OK + assert!(Ledger::::get(&444).is_none()); // OK + + // side effects on 444 - ledger, bonded, payee, lock should remain be intact. + // however, 444 lock was removed. + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), 0); // NOK + assert!(Bonded::::get(&444).is_some()); // OK + assert!(Payee::::get(&444).is_some()); // OK + assert!(Ledger::::get(&555).is_none()); // NOK + + assert!(Staking::do_try_state(System::block_number()).is_err()); + }) + } + + #[test] + fn inspect_recovery_ledger_corupted_killed_other_works() { + ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333); + + // get into corrupted and killed ledger state by killing a corrupted ledger: + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + // kill(444) + // (333, 444) -> corrupted and None + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + + // now try-state fails. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // 333 is corrupted since it's controller is linking 444 ledger. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted); + // 444 however is OK. + assert_eq!(Staking::inspect_bond_state(&444).unwrap(), LedgerIntegrityState::Ok); + + // kill the *other* ledger that is double bonded but not corrupted. + assert_ok!(StakingLedger::::kill(&444)); + + // now 333 is corrupted and None through the *other* ledger being killed. + assert_eq!( + Staking::inspect_bond_state(&333).unwrap(), + LedgerIntegrityState::CorruptedKilled, + ); + // 444 is cleaned and not a stash anymore; no lock left behind. + assert_eq!(Ledger::::get(&444), None); + assert_eq!(Staking::inspect_bond_state(&444), Err(Error::::NotStash)); + + // side effects on 333 - ledger, bonded, payee, lock should be intact. + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); // OK + assert_eq!(Bonded::::get(&333), Some(444)); // OK + assert!(Payee::::get(&333).is_some()); // OK + // however, ledger associated with its controller was killed. + assert!(Ledger::::get(&444).is_none()); // NOK + + // side effects on 444 - ledger, bonded, payee, lock should be completely removed. + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), 0); // OK + assert!(Bonded::::get(&444).is_none()); // OK + assert!(Payee::::get(&444).is_none()); // OK + assert!(Ledger::::get(&555).is_none()); // OK + + assert!(Staking::do_try_state(System::block_number()).is_err()); + }) + } + + #[test] + fn inspect_recovery_ledger_lock_corrupted_works() { + ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // get into lock corrupted ledger state by bond_extra on a ledger that is double bonded + // with a corrupted ledger. + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + // bond_extra(333, 10) -> lock corrupted on 444 + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + bond_extra_no_checks(&333, 10); + + // now try-state fails. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // 333 is corrupted since it's controller is linking 444 ledger. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted); + // 444 ledger is not corrupted but locks got out of sync. + assert_eq!( + Staking::inspect_bond_state(&444).unwrap(), + LedgerIntegrityState::LockCorrupted + ); + }) + } + + // Corrupted ledger restore. + // + // * Double bonded and corrupted ledger. + #[test] + fn restore_ledger_corrupted_works() { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // get into corrupted and killed ledger state. + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted); + + // now try-state fails. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // recover the ledger bonded by 333 stash. + assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None)); + + // try-state checks are ok now. + assert_ok!(Staking::do_try_state(System::block_number())); + }) + } + + // Corrupted and killed ledger restore. + // + // * Double bonded and corrupted ledger. + // * Ledger killed by own controller. + #[test] + fn restore_ledger_corrupted_killed_works() { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // ledger.total == lock + let total_444_before_corruption = Balances::balance_locked(crate::STAKING_ID, &444); + + // get into corrupted and killed ledger state by killing a corrupted ledger: + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + // kill(333) + // (444, 444) -> corrupted and None. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + + // kill the corrupted ledger that is associated with stash 333. + assert_ok!(StakingLedger::::kill(&333)); + + // 333 bond is no more but it returns `BadState` because the lock on this stash is + // still set (see checks below). + assert_eq!(Staking::inspect_bond_state(&333), Err(Error::::BadState)); + // now the *other* ledger associated with 444 has been corrupted and killed (None). + assert!(Staking::ledger(StakingAccount::Stash(444)).is_err()); + + // try-state should fail. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // recover the ledger bonded by 333 stash. + assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None)); + + // for the try-state checks to pass, we also need to recover the stash 444 which is + // corrupted too by proxy of kill(333). Currently, both the lock and the ledger of 444 + // have been cleared so we need to provide the new amount to restore the ledger. + assert_noop!( + Staking::restore_ledger(RuntimeOrigin::root(), 444, None, None, None), + Error::::CannotRestoreLedger + ); + + assert_ok!(Staking::restore_ledger( + RuntimeOrigin::root(), + 444, + None, + Some(total_444_before_corruption), + None, + )); + + // try-state checks are ok now. + assert_ok!(Staking::do_try_state(System::block_number())); + }) + } + + // Corrupted and killed by *other* ledger restore. + // + // * Double bonded and corrupted ledger. + // * Ledger killed by own controller. + #[test] + fn restore_ledger_corrupted_killed_other_works() { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // get into corrupted and killed ledger state by killing a corrupted ledger: + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + // kill(444) + // (333, 444) -> corrupted and None + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + + // now try-state fails. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // 333 is corrupted since it's controller is linking 444 ledger. + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted); + // 444 however is OK. + assert_eq!(Staking::inspect_bond_state(&444).unwrap(), LedgerIntegrityState::Ok); + + // kill the *other* ledger that is double bonded but not corrupted. + assert_ok!(StakingLedger::::kill(&444)); + + // recover the ledger bonded by 333 stash. + assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None)); + + // 444 does not need recover in this case since it's been killed successfully. + assert_eq!(Staking::inspect_bond_state(&444), Err(Error::::NotStash)); + + // try-state checks are ok now. + assert_ok!(Staking::do_try_state(System::block_number())); + }) + } + + // Corrupted with bond_extra. + // + // * Double bonded and corrupted ledger. + // * Corrupted ledger calls `bond_extra` + #[test] + fn restore_ledger_corrupted_bond_extra_works() { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + setup_double_bonded_ledgers(); + + let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333); + let lock_444_before = Balances::balance_locked(crate::STAKING_ID, &444); + + // get into corrupted and killed ledger state by killing a corrupted ledger: + // init state: + // (333, 444) + // (444, 555) + // set_controller(444) to 444 + // (333, 444) -> corrupted + // (444, 444) + // bond_extra(444, 40) -> OK + // bond_extra(333, 30) -> locks out of sync + + assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok); + set_controller_no_checks(&444); + + // now try-state fails. + assert!(Staking::do_try_state(System::block_number()).is_err()); + + // if 444 bonds extra, the locks remain in sync. + bond_extra_no_checks(&444, 40); + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), lock_444_before + 40); + + // however if 333 bonds extra, the wrong lock is updated. + bond_extra_no_checks(&333, 30); + assert_eq!( + Balances::balance_locked(crate::STAKING_ID, &333), + lock_444_before + 40 + 30 + ); //not OK + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), lock_444_before + 40); // OK + + // recover the ledger bonded by 333 stash. Note that the total/lock needs to be + // re-written since on-chain data lock has become out of sync. + assert_ok!(Staking::restore_ledger( + RuntimeOrigin::root(), + 333, + None, + Some(lock_333_before + 30), + None + )); + + // now recover 444 that although it's not corrupted, its lock and ledger.total are out + // of sync. in which case, we need to explicitly set the ledger's lock and amount, + // otherwise the ledger recover will fail. + assert_noop!( + Staking::restore_ledger(RuntimeOrigin::root(), 444, None, None, None), + Error::::CannotRestoreLedger + ); + + //and enforcing a new ledger lock/total on this non-corrupted ledger will work. + assert_ok!(Staking::restore_ledger( + RuntimeOrigin::root(), + 444, + None, + Some(lock_444_before + 40), + None + )); + + // double-check that ledgers got to expected state and bond_extra done during the + // corrupted state is part of the recovered ledgers. + let ledger_333 = Bonded::::get(&333).and_then(Ledger::::get).unwrap(); + let ledger_444 = Bonded::::get(&444).and_then(Ledger::::get).unwrap(); + + assert_eq!(ledger_333.total, lock_333_before + 30); + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), ledger_333.total); + assert_eq!(ledger_444.total, lock_444_before + 40); + assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), ledger_444.total); + + // try-state checks are ok now. + assert_ok!(Staking::do_try_state(System::block_number())); }) } } diff --git a/substrate/frame/staking/src/weights.rs b/substrate/frame/staking/src/weights.rs index 6f729e08ba5c..8a04a3dfb3f7 100644 --- a/substrate/frame/staking/src/weights.rs +++ b/substrate/frame/staking/src/weights.rs @@ -17,10 +17,10 @@ //! Autogenerated weights for `pallet_staking` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-q7z7ruxr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -80,6 +80,7 @@ pub trait WeightInfo { fn chill_other() -> Weight; fn force_apply_min_commission() -> Weight; fn set_min_commission() -> Weight; + fn restore_ledger() -> Weight; } /// Weights for `pallet_staking` using the Substrate node and recommended hardware. @@ -87,21 +88,21 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - /// Storage: `Staking::Ledger` (r:0 w:1) - /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:0 w:1) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `927` + // Measured: `1042` // Estimated: `4764` - // Minimum execution time: 42_042_000 picoseconds. - Weight::from_parts(43_292_000, 4764) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Minimum execution time: 48_753_000 picoseconds. + Weight::from_parts(50_539_000, 4764) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) @@ -120,21 +121,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1990` // Estimated: `8877` - // Minimum execution time: 85_050_000 picoseconds. - Weight::from_parts(87_567_000, 8877) + // Minimum execution time: 92_701_000 picoseconds. + Weight::from_parts(95_657_000, 8877) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) @@ -147,41 +148,43 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2195` // Estimated: `8877` - // Minimum execution time: 89_076_000 picoseconds. - Weight::from_parts(92_715_000, 8877) + // Minimum execution time: 101_049_000 picoseconds. + Weight::from_parts(103_729_000, 8877) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Staking::CurrentEra` (r:1 w:0) - /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1115` + // Measured: `1297` // Estimated: `4764` - // Minimum execution time: 42_067_000 picoseconds. - Weight::from_parts(43_239_807, 4764) - // Standard Error: 831 - .saturating_add(Weight::from_parts(46_257, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 51_672_000 picoseconds. + Weight::from_parts(53_817_441, 4764) + // Standard Error: 1_124 + .saturating_add(Weight::from_parts(49_168, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:1) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Staking::Bonded` (r:1 w:1) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) @@ -207,10 +210,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 86_490_000 picoseconds. - Weight::from_parts(95_358_751, 6248) - // Standard Error: 3_952 - .saturating_add(Weight::from_parts(1_294_907, 0).saturating_mul(s.into())) + // Minimum execution time: 92_846_000 picoseconds. + Weight::from_parts(102_158_606, 6248) + // Standard Error: 4_187 + .saturating_add(Weight::from_parts(1_436_364, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -218,6 +221,8 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::MinValidatorBond` (r:1 w:0) /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MinCommission` (r:1 w:0) @@ -228,8 +233,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:1 w:1) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:1 w:1) @@ -242,31 +245,35 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1372` // Estimated: `4556` - // Minimum execution time: 50_326_000 picoseconds. - Weight::from_parts(52_253_000, 4556) + // Minimum execution time: 58_162_000 picoseconds. + Weight::from_parts(60_124_000, 4556) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:128 w:128) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1280 + k * (569 ±0)` + // Measured: `1815 + k * (572 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 29_305_000 picoseconds. - Weight::from_parts(32_199_604, 4556) - // Standard Error: 7_150 - .saturating_add(Weight::from_parts(6_437_124, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Minimum execution time: 37_950_000 picoseconds. + Weight::from_parts(34_461_075, 4556) + // Standard Error: 8_013 + .saturating_add(Weight::from_parts(6_696_510, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -277,8 +284,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:2 w:2) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:1 w:1) @@ -292,10 +297,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1866 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 63_267_000 picoseconds. - Weight::from_parts(61_741_404, 6248) - // Standard Error: 12_955 - .saturating_add(Weight::from_parts(3_811_743, 0).saturating_mul(n.into())) + // Minimum execution time: 70_167_000 picoseconds. + Weight::from_parts(68_024_084, 6248) + // Standard Error: 14_256 + .saturating_add(Weight::from_parts(4_195_757, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -303,6 +308,8 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -317,11 +324,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1650` + // Measured: `1816` // Estimated: `6248` - // Minimum execution time: 52_862_000 picoseconds. - Weight::from_parts(54_108_000, 6248) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Minimum execution time: 61_730_000 picoseconds. + Weight::from_parts(63_430_000, 6248) + .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `Staking::Ledger` (r:1 w:0) @@ -334,37 +341,37 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `902` // Estimated: `4556` - // Minimum execution time: 16_350_000 picoseconds. - Weight::from_parts(16_802_000, 4556) + // Minimum execution time: 20_857_000 picoseconds. + Weight::from_parts(21_615_000, 4556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Staking::Payee` (r:1 w:1) - /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Payee` (r:1 w:1) + /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_payee() -> Weight { // Proof Size summary in bytes: // Measured: `969` // Estimated: `4556` - // Minimum execution time: 19_981_000 picoseconds. - Weight::from_parts(20_539_000, 4556) + // Minimum execution time: 24_739_000 picoseconds. + Weight::from_parts(25_785_000, 4556) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Staking::Ledger` (r:1 w:2) + /// Storage: `Staking::Ledger` (r:2 w:2) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) fn set_controller() -> Weight { // Proof Size summary in bytes: // Measured: `902` - // Estimated: `4556` - // Minimum execution time: 19_304_000 picoseconds. - Weight::from_parts(20_000_000, 4556) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Estimated: `8122` + // Minimum execution time: 24_622_000 picoseconds. + Weight::from_parts(25_220_000, 8122) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Staking::ValidatorCount` (r:0 w:1) @@ -373,8 +380,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_568_000 picoseconds. - Weight::from_parts(2_708_000, 0) + // Minimum execution time: 2_634_000 picoseconds. + Weight::from_parts(2_842_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -383,8 +390,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_950_000 picoseconds. - Weight::from_parts(8_348_000, 0) + // Minimum execution time: 8_496_000 picoseconds. + Weight::from_parts(9_016_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -393,8 +400,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_967_000 picoseconds. - Weight::from_parts(8_222_000, 0) + // Minimum execution time: 8_510_000 picoseconds. + Weight::from_parts(8_893_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -403,8 +410,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_006_000 picoseconds. - Weight::from_parts(8_440_000, 0) + // Minimum execution time: 8_243_000 picoseconds. + Weight::from_parts(8_678_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Invulnerables` (r:0 w:1) @@ -414,30 +421,30 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_524_000 picoseconds. - Weight::from_parts(3_123_608, 0) - // Standard Error: 59 - .saturating_add(Weight::from_parts(11_596, 0).saturating_mul(v.into())) + // Minimum execution time: 2_781_000 picoseconds. + Weight::from_parts(3_441_708, 0) + // Standard Error: 58 + .saturating_add(Weight::from_parts(11_811, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Staking::Ledger` (r:5900 w:11800) + /// Storage: `Staking::Ledger` (r:11800 w:11800) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:5900 w:5900) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:5900 w:0) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:0 w:5900) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 5900]`. fn deprecate_controller_batch(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1356 + i * (151 ±0)` - // Estimated: `990 + i * (3566 ±0)` - // Minimum execution time: 2_092_000 picoseconds. - Weight::from_parts(2_258_000, 990) - // Standard Error: 32_695 - .saturating_add(Weight::from_parts(16_669_219, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into()))) + // Measured: `1746 + i * (229 ±0)` + // Estimated: `990 + i * (7132 ±0)` + // Minimum execution time: 5_331_000 picoseconds. + Weight::from_parts(5_511_000, 990) + // Standard Error: 66_734 + .saturating_add(Weight::from_parts(31_157_413, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into())) } /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -472,10 +479,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 84_275_000 picoseconds. - Weight::from_parts(92_512_416, 6248) - // Standard Error: 3_633 - .saturating_add(Weight::from_parts(1_315_923, 0).saturating_mul(s.into())) + // Minimum execution time: 89_473_000 picoseconds. + Weight::from_parts(98_055_990, 6248) + // Standard Error: 4_159 + .saturating_add(Weight::from_parts(1_398_203, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -488,10 +495,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `66672` // Estimated: `70137` - // Minimum execution time: 101_707_000 picoseconds. - Weight::from_parts(912_819_462, 70137) - // Standard Error: 57_547 - .saturating_add(Weight::from_parts(4_856_799, 0).saturating_mul(s.into())) + // Minimum execution time: 102_480_000 picoseconds. + Weight::from_parts(1_165_789_820, 70137) + // Standard Error: 77_157 + .saturating_add(Weight::from_parts(6_489_253, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -528,10 +535,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `33297 + n * (377 ±0)` // Estimated: `30944 + n * (3774 ±0)` - // Minimum execution time: 138_657_000 picoseconds. - Weight::from_parts(167_173_445, 30944) - // Standard Error: 25_130 - .saturating_add(Weight::from_parts(44_566_012, 0).saturating_mul(n.into())) + // Minimum execution time: 156_890_000 picoseconds. + Weight::from_parts(202_972_688, 30944) + // Standard Error: 29_972 + .saturating_add(Weight::from_parts(48_226_698, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -555,10 +562,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1991 + l * (7 ±0)` // Estimated: `8877` - // Minimum execution time: 80_061_000 picoseconds. - Weight::from_parts(82_836_434, 8877) - // Standard Error: 4_348 - .saturating_add(Weight::from_parts(75_744, 0).saturating_mul(l.into())) + // Minimum execution time: 88_482_000 picoseconds. + Weight::from_parts(92_616_600, 8877) + // Standard Error: 4_411 + .saturating_add(Weight::from_parts(117_722, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -593,10 +600,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 92_560_000 picoseconds. - Weight::from_parts(97_684_741, 6248) - // Standard Error: 3_361 - .saturating_add(Weight::from_parts(1_292_732, 0).saturating_mul(s.into())) + // Minimum execution time: 98_489_000 picoseconds. + Weight::from_parts(102_968_643, 6248) + // Standard Error: 4_823 + .saturating_add(Weight::from_parts(1_420_838, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -642,12 +649,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 564_963_000 picoseconds. - Weight::from_parts(569_206_000, 512390) - // Standard Error: 2_033_235 - .saturating_add(Weight::from_parts(68_025_841, 0).saturating_mul(v.into())) - // Standard Error: 202_600 - .saturating_add(Weight::from_parts(17_916_770, 0).saturating_mul(n.into())) + // Minimum execution time: 604_820_000 picoseconds. + Weight::from_parts(608_838_000, 512390) + // Standard Error: 2_300_345 + .saturating_add(Weight::from_parts(72_980_573, 0).saturating_mul(v.into())) + // Standard Error: 229_216 + .saturating_add(Weight::from_parts(20_739_416, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -678,12 +685,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3175 + n * (911 ±0) + v * (395 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 32_196_540_000 picoseconds. - Weight::from_parts(32_341_871_000, 512390) - // Standard Error: 354_657 - .saturating_add(Weight::from_parts(5_143_440, 0).saturating_mul(v.into())) - // Standard Error: 354_657 - .saturating_add(Weight::from_parts(3_328_189, 0).saturating_mul(n.into())) + // Minimum execution time: 37_380_439_000 picoseconds. + Weight::from_parts(38_187_734_000, 512390) + // Standard Error: 425_319 + .saturating_add(Weight::from_parts(6_001_288, 0).saturating_mul(v.into())) + // Standard Error: 425_319 + .saturating_add(Weight::from_parts(4_129_446, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -700,10 +707,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `979 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_381_903_000 picoseconds. - Weight::from_parts(32_693_059, 3510) - // Standard Error: 10_000 - .saturating_add(Weight::from_parts(4_736_173, 0).saturating_mul(v.into())) + // Minimum execution time: 2_572_838_000 picoseconds. + Weight::from_parts(67_632_557, 3510) + // Standard Error: 12_028 + .saturating_add(Weight::from_parts(5_117_459, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -714,6 +721,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -724,9 +733,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_434_000 picoseconds. - Weight::from_parts(5_742_000, 0) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Minimum execution time: 5_962_000 picoseconds. + Weight::from_parts(6_497_000, 0) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::MinCommission` (r:0 w:1) /// Proof: `Staking::MinCommission` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -734,6 +743,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -744,9 +755,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_588_000 picoseconds. - Weight::from_parts(4_854_000, 0) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Minimum execution time: 5_227_000 picoseconds. + Weight::from_parts(5_496_000, 0) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) @@ -774,8 +785,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1939` // Estimated: `6248` - // Minimum execution time: 68_780_000 picoseconds. - Weight::from_parts(71_479_000, 6248) + // Minimum execution time: 75_129_000 picoseconds. + Weight::from_parts(77_498_000, 6248) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -787,8 +798,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `691` // Estimated: `3510` - // Minimum execution time: 12_268_000 picoseconds. - Weight::from_parts(12_661_000, 3510) + // Minimum execution time: 13_488_000 picoseconds. + Weight::from_parts(14_183_000, 3510) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -798,31 +809,50 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_071_000 picoseconds. - Weight::from_parts(3_334_000, 0) + // Minimum execution time: 3_368_000 picoseconds. + Weight::from_parts(3_582_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:1) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + fn restore_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `1047` + // Estimated: `4764` + // Minimum execution time: 44_876_000 picoseconds. + Weight::from_parts(46_353_000, 4764) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } } // For backwards compatibility and tests. impl WeightInfo for () { /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - /// Storage: `Staking::Ledger` (r:0 w:1) - /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:0 w:1) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `927` + // Measured: `1042` // Estimated: `4764` - // Minimum execution time: 42_042_000 picoseconds. - Weight::from_parts(43_292_000, 4764) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Minimum execution time: 48_753_000 picoseconds. + Weight::from_parts(50_539_000, 4764) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) @@ -841,21 +871,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1990` // Estimated: `8877` - // Minimum execution time: 85_050_000 picoseconds. - Weight::from_parts(87_567_000, 8877) + // Minimum execution time: 92_701_000 picoseconds. + Weight::from_parts(95_657_000, 8877) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) @@ -868,41 +898,43 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2195` // Estimated: `8877` - // Minimum execution time: 89_076_000 picoseconds. - Weight::from_parts(92_715_000, 8877) + // Minimum execution time: 101_049_000 picoseconds. + Weight::from_parts(103_729_000, 8877) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Staking::CurrentEra` (r:1 w:0) - /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1115` + // Measured: `1297` // Estimated: `4764` - // Minimum execution time: 42_067_000 picoseconds. - Weight::from_parts(43_239_807, 4764) - // Standard Error: 831 - .saturating_add(Weight::from_parts(46_257, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Minimum execution time: 51_672_000 picoseconds. + Weight::from_parts(53_817_441, 4764) + // Standard Error: 1_124 + .saturating_add(Weight::from_parts(49_168, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:1) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Staking::Bonded` (r:1 w:1) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) @@ -928,10 +960,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 86_490_000 picoseconds. - Weight::from_parts(95_358_751, 6248) - // Standard Error: 3_952 - .saturating_add(Weight::from_parts(1_294_907, 0).saturating_mul(s.into())) + // Minimum execution time: 92_846_000 picoseconds. + Weight::from_parts(102_158_606, 6248) + // Standard Error: 4_187 + .saturating_add(Weight::from_parts(1_436_364, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -939,6 +971,8 @@ impl WeightInfo for () { } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::MinValidatorBond` (r:1 w:0) /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MinCommission` (r:1 w:0) @@ -949,8 +983,6 @@ impl WeightInfo for () { /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:1 w:1) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:1 w:1) @@ -963,31 +995,35 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1372` // Estimated: `4556` - // Minimum execution time: 50_326_000 picoseconds. - Weight::from_parts(52_253_000, 4556) + // Minimum execution time: 58_162_000 picoseconds. + Weight::from_parts(60_124_000, 4556) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:128 w:128) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1280 + k * (569 ±0)` + // Measured: `1815 + k * (572 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 29_305_000 picoseconds. - Weight::from_parts(32_199_604, 4556) - // Standard Error: 7_150 - .saturating_add(Weight::from_parts(6_437_124, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Minimum execution time: 37_950_000 picoseconds. + Weight::from_parts(34_461_075, 4556) + // Standard Error: 8_013 + .saturating_add(Weight::from_parts(6_696_510, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 3033).saturating_mul(k.into())) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -998,8 +1034,6 @@ impl WeightInfo for () { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:1 w:0) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:2 w:2) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:1 w:1) @@ -1013,10 +1047,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1866 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 63_267_000 picoseconds. - Weight::from_parts(61_741_404, 6248) - // Standard Error: 12_955 - .saturating_add(Weight::from_parts(3_811_743, 0).saturating_mul(n.into())) + // Minimum execution time: 70_167_000 picoseconds. + Weight::from_parts(68_024_084, 6248) + // Standard Error: 14_256 + .saturating_add(Weight::from_parts(4_195_757, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1024,6 +1058,8 @@ impl WeightInfo for () { } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -1038,11 +1074,11 @@ impl WeightInfo for () { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1650` + // Measured: `1816` // Estimated: `6248` - // Minimum execution time: 52_862_000 picoseconds. - Weight::from_parts(54_108_000, 6248) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Minimum execution time: 61_730_000 picoseconds. + Weight::from_parts(63_430_000, 6248) + .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `Staking::Ledger` (r:1 w:0) @@ -1055,37 +1091,37 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `902` // Estimated: `4556` - // Minimum execution time: 16_350_000 picoseconds. - Weight::from_parts(16_802_000, 4556) + // Minimum execution time: 20_857_000 picoseconds. + Weight::from_parts(21_615_000, 4556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Staking::Payee` (r:1 w:1) - /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Payee` (r:1 w:1) + /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_payee() -> Weight { // Proof Size summary in bytes: // Measured: `969` // Estimated: `4556` - // Minimum execution time: 19_981_000 picoseconds. - Weight::from_parts(20_539_000, 4556) + // Minimum execution time: 24_739_000 picoseconds. + Weight::from_parts(25_785_000, 4556) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Staking::Ledger` (r:1 w:2) + /// Storage: `Staking::Ledger` (r:2 w:2) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) fn set_controller() -> Weight { // Proof Size summary in bytes: // Measured: `902` - // Estimated: `4556` - // Minimum execution time: 19_304_000 picoseconds. - Weight::from_parts(20_000_000, 4556) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Estimated: `8122` + // Minimum execution time: 24_622_000 picoseconds. + Weight::from_parts(25_220_000, 8122) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Staking::ValidatorCount` (r:0 w:1) @@ -1094,8 +1130,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_568_000 picoseconds. - Weight::from_parts(2_708_000, 0) + // Minimum execution time: 2_634_000 picoseconds. + Weight::from_parts(2_842_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1104,8 +1140,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_950_000 picoseconds. - Weight::from_parts(8_348_000, 0) + // Minimum execution time: 8_496_000 picoseconds. + Weight::from_parts(9_016_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1114,8 +1150,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_967_000 picoseconds. - Weight::from_parts(8_222_000, 0) + // Minimum execution time: 8_510_000 picoseconds. + Weight::from_parts(8_893_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1124,8 +1160,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_006_000 picoseconds. - Weight::from_parts(8_440_000, 0) + // Minimum execution time: 8_243_000 picoseconds. + Weight::from_parts(8_678_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Invulnerables` (r:0 w:1) @@ -1135,30 +1171,30 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_524_000 picoseconds. - Weight::from_parts(3_123_608, 0) - // Standard Error: 59 - .saturating_add(Weight::from_parts(11_596, 0).saturating_mul(v.into())) + // Minimum execution time: 2_781_000 picoseconds. + Weight::from_parts(3_441_708, 0) + // Standard Error: 58 + .saturating_add(Weight::from_parts(11_811, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Staking::Ledger` (r:5900 w:11800) + /// Storage: `Staking::Ledger` (r:11800 w:11800) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:5900 w:5900) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:5900 w:0) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Staking::Bonded` (r:0 w:5900) - /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 5900]`. fn deprecate_controller_batch(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1356 + i * (151 ±0)` - // Estimated: `990 + i * (3566 ±0)` - // Minimum execution time: 2_092_000 picoseconds. - Weight::from_parts(2_258_000, 990) - // Standard Error: 32_695 - .saturating_add(Weight::from_parts(16_669_219, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(i.into()))) + // Measured: `1746 + i * (229 ±0)` + // Estimated: `990 + i * (7132 ±0)` + // Minimum execution time: 5_331_000 picoseconds. + Weight::from_parts(5_511_000, 990) + // Standard Error: 66_734 + .saturating_add(Weight::from_parts(31_157_413, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(i.into()))) - .saturating_add(Weight::from_parts(0, 3566).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into())) } /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1193,10 +1229,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 84_275_000 picoseconds. - Weight::from_parts(92_512_416, 6248) - // Standard Error: 3_633 - .saturating_add(Weight::from_parts(1_315_923, 0).saturating_mul(s.into())) + // Minimum execution time: 89_473_000 picoseconds. + Weight::from_parts(98_055_990, 6248) + // Standard Error: 4_159 + .saturating_add(Weight::from_parts(1_398_203, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1209,10 +1245,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `66672` // Estimated: `70137` - // Minimum execution time: 101_707_000 picoseconds. - Weight::from_parts(912_819_462, 70137) - // Standard Error: 57_547 - .saturating_add(Weight::from_parts(4_856_799, 0).saturating_mul(s.into())) + // Minimum execution time: 102_480_000 picoseconds. + Weight::from_parts(1_165_789_820, 70137) + // Standard Error: 77_157 + .saturating_add(Weight::from_parts(6_489_253, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1249,10 +1285,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `33297 + n * (377 ±0)` // Estimated: `30944 + n * (3774 ±0)` - // Minimum execution time: 138_657_000 picoseconds. - Weight::from_parts(167_173_445, 30944) - // Standard Error: 25_130 - .saturating_add(Weight::from_parts(44_566_012, 0).saturating_mul(n.into())) + // Minimum execution time: 156_890_000 picoseconds. + Weight::from_parts(202_972_688, 30944) + // Standard Error: 29_972 + .saturating_add(Weight::from_parts(48_226_698, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -1276,10 +1312,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1991 + l * (7 ±0)` // Estimated: `8877` - // Minimum execution time: 80_061_000 picoseconds. - Weight::from_parts(82_836_434, 8877) - // Standard Error: 4_348 - .saturating_add(Weight::from_parts(75_744, 0).saturating_mul(l.into())) + // Minimum execution time: 88_482_000 picoseconds. + Weight::from_parts(92_616_600, 8877) + // Standard Error: 4_411 + .saturating_add(Weight::from_parts(117_722, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -1314,10 +1350,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 92_560_000 picoseconds. - Weight::from_parts(97_684_741, 6248) - // Standard Error: 3_361 - .saturating_add(Weight::from_parts(1_292_732, 0).saturating_mul(s.into())) + // Minimum execution time: 98_489_000 picoseconds. + Weight::from_parts(102_968_643, 6248) + // Standard Error: 4_823 + .saturating_add(Weight::from_parts(1_420_838, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1363,12 +1399,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 564_963_000 picoseconds. - Weight::from_parts(569_206_000, 512390) - // Standard Error: 2_033_235 - .saturating_add(Weight::from_parts(68_025_841, 0).saturating_mul(v.into())) - // Standard Error: 202_600 - .saturating_add(Weight::from_parts(17_916_770, 0).saturating_mul(n.into())) + // Minimum execution time: 604_820_000 picoseconds. + Weight::from_parts(608_838_000, 512390) + // Standard Error: 2_300_345 + .saturating_add(Weight::from_parts(72_980_573, 0).saturating_mul(v.into())) + // Standard Error: 229_216 + .saturating_add(Weight::from_parts(20_739_416, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1399,12 +1435,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3175 + n * (911 ±0) + v * (395 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 32_196_540_000 picoseconds. - Weight::from_parts(32_341_871_000, 512390) - // Standard Error: 354_657 - .saturating_add(Weight::from_parts(5_143_440, 0).saturating_mul(v.into())) - // Standard Error: 354_657 - .saturating_add(Weight::from_parts(3_328_189, 0).saturating_mul(n.into())) + // Minimum execution time: 37_380_439_000 picoseconds. + Weight::from_parts(38_187_734_000, 512390) + // Standard Error: 425_319 + .saturating_add(Weight::from_parts(6_001_288, 0).saturating_mul(v.into())) + // Standard Error: 425_319 + .saturating_add(Weight::from_parts(4_129_446, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1421,10 +1457,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `979 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_381_903_000 picoseconds. - Weight::from_parts(32_693_059, 3510) - // Standard Error: 10_000 - .saturating_add(Weight::from_parts(4_736_173, 0).saturating_mul(v.into())) + // Minimum execution time: 2_572_838_000 picoseconds. + Weight::from_parts(67_632_557, 3510) + // Standard Error: 12_028 + .saturating_add(Weight::from_parts(5_117_459, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -1435,6 +1471,8 @@ impl WeightInfo for () { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -1445,9 +1483,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_434_000 picoseconds. - Weight::from_parts(5_742_000, 0) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Minimum execution time: 5_962_000 picoseconds. + Weight::from_parts(6_497_000, 0) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::MinCommission` (r:0 w:1) /// Proof: `Staking::MinCommission` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -1455,6 +1493,8 @@ impl WeightInfo for () { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -1465,9 +1505,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_588_000 picoseconds. - Weight::from_parts(4_854_000, 0) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Minimum execution time: 5_227_000 picoseconds. + Weight::from_parts(5_496_000, 0) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) @@ -1495,8 +1535,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1939` // Estimated: `6248` - // Minimum execution time: 68_780_000 picoseconds. - Weight::from_parts(71_479_000, 6248) + // Minimum execution time: 75_129_000 picoseconds. + Weight::from_parts(77_498_000, 6248) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1508,8 +1548,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `691` // Estimated: `3510` - // Minimum execution time: 12_268_000 picoseconds. - Weight::from_parts(12_661_000, 3510) + // Minimum execution time: 13_488_000 picoseconds. + Weight::from_parts(14_183_000, 3510) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1519,8 +1559,27 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_071_000 picoseconds. - Weight::from_parts(3_334_000, 0) + // Minimum execution time: 3_368_000 picoseconds. + Weight::from_parts(3_582_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:1) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + fn restore_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `1047` + // Estimated: `4764` + // Minimum execution time: 44_876_000 picoseconds. + Weight::from_parts(46_353_000, 4764) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } } diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 1997d8fc223e..24e7e1c8a65c 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -22,8 +22,8 @@ pub mod tokens; pub use tokens::{ currency::{ - ActiveIssuanceOf, Currency, LockIdentifier, LockableCurrency, NamedReservableCurrency, - ReservableCurrency, TotalIssuanceOf, VestingSchedule, + ActiveIssuanceOf, Currency, InspectLockableCurrency, LockIdentifier, LockableCurrency, + NamedReservableCurrency, ReservableCurrency, TotalIssuanceOf, VestingSchedule, }, fungible, fungibles, imbalance::{Imbalance, OnUnbalanced, SignedImbalance}, diff --git a/substrate/frame/support/src/traits/schedule.rs b/substrate/frame/support/src/traits/schedule.rs index 7a7d1357da1e..f41c73fe69a8 100644 --- a/substrate/frame/support/src/traits/schedule.rs +++ b/substrate/frame/support/src/traits/schedule.rs @@ -130,7 +130,7 @@ impl MaybeHashed { } } -// TODO: deprecate +#[deprecated(note = "Use `v3` instead. Will be removed after September 2024.")] pub mod v1 { use super::*; @@ -218,10 +218,12 @@ pub mod v1 { fn next_dispatch_time(id: Vec) -> Result; } + #[allow(deprecated)] impl Anon for T where T: v2::Anon, { + #[allow(deprecated)] type Address = T::Address; fn schedule( @@ -232,10 +234,13 @@ pub mod v1 { call: Call, ) -> Result { let c = MaybeHashed::::Value(call); + + #[allow(deprecated)] T::schedule(when, maybe_periodic, priority, origin, c) } fn cancel(address: Self::Address) -> Result<(), ()> { + #[allow(deprecated)] T::cancel(address) } @@ -243,18 +248,22 @@ pub mod v1 { address: Self::Address, when: DispatchTime, ) -> Result { + #[allow(deprecated)] T::reschedule(address, when) } fn next_dispatch_time(address: Self::Address) -> Result { + #[allow(deprecated)] T::next_dispatch_time(address) } } + #[allow(deprecated)] impl Named for T where T: v2::Named, { + #[allow(deprecated)] type Address = T::Address; fn schedule_named( @@ -266,10 +275,12 @@ pub mod v1 { call: Call, ) -> Result { let c = MaybeHashed::::Value(call); + #[allow(deprecated)] T::schedule_named(id, when, maybe_periodic, priority, origin, c) } fn cancel_named(id: Vec) -> Result<(), ()> { + #[allow(deprecated)] T::cancel_named(id) } @@ -277,16 +288,18 @@ pub mod v1 { id: Vec, when: DispatchTime, ) -> Result { + #[allow(deprecated)] T::reschedule_named(id, when) } fn next_dispatch_time(id: Vec) -> Result { + #[allow(deprecated)] T::next_dispatch_time(id) } } } -// TODO: deprecate +#[deprecated(note = "Use `v3` instead. Will be removed after September 2024.")] pub mod v2 { use super::*; @@ -478,4 +491,5 @@ pub mod v3 { } } +#[allow(deprecated)] pub use v1::*; diff --git a/substrate/frame/support/src/traits/tokens/currency.rs b/substrate/frame/support/src/traits/tokens/currency.rs index 8b773115011d..282e7f644733 100644 --- a/substrate/frame/support/src/traits/tokens/currency.rs +++ b/substrate/frame/support/src/traits/tokens/currency.rs @@ -27,7 +27,7 @@ use sp_runtime::{traits::MaybeSerializeDeserialize, DispatchError}; mod reservable; pub use reservable::{NamedReservableCurrency, ReservableCurrency}; mod lockable; -pub use lockable::{LockIdentifier, LockableCurrency, VestingSchedule}; +pub use lockable::{InspectLockableCurrency, LockIdentifier, LockableCurrency, VestingSchedule}; /// Abstraction over a fungible assets system. pub trait Currency { diff --git a/substrate/frame/support/src/traits/tokens/currency/lockable.rs b/substrate/frame/support/src/traits/tokens/currency/lockable.rs index 955814f5aa9d..51a48dd15ce8 100644 --- a/substrate/frame/support/src/traits/tokens/currency/lockable.rs +++ b/substrate/frame/support/src/traits/tokens/currency/lockable.rs @@ -64,6 +64,12 @@ pub trait LockableCurrency: Currency { fn remove_lock(id: LockIdentifier, who: &AccountId); } +/// A inspect interface for a currency whose accounts can have liquidity restrictions. +pub trait InspectLockableCurrency: LockableCurrency { + /// Amount of funds locked for `who` associated with `id`. + fn balance_locked(id: LockIdentifier, who: &AccountId) -> Self::Balance; +} + /// A vesting schedule over a currency. This allows a particular currency to have vesting limits /// applied to it. pub trait VestingSchedule { diff --git a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs index b7e5600a017a..87a381fd7bf9 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -797,7 +797,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { } Ok(quote!( - const RUNTIME_API_VERSIONS: #c::ApisVec = #c::create_apis_vec!([ #( #result ),* ]); + pub const RUNTIME_API_VERSIONS: #c::ApisVec = #c::create_apis_vec!([ #( #result ),* ]); #( #sections )* )) diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index 4dd24803e9b1..bb4a53949587 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -6,8 +6,8 @@ use std::{sync::Arc, time::Duration}; use cumulus_client_cli::CollatorOptions; // Local Runtime Types use parachain_template_runtime::{ + apis::RuntimeApi, opaque::{Block, Hash}, - RuntimeApi, }; // Cumulus Imports @@ -46,7 +46,7 @@ impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor { ); fn dispatch(method: &str, data: &[u8]) -> Option> { - parachain_template_runtime::api::dispatch(method, data) + parachain_template_runtime::apis::api::dispatch(method, data) } fn native_version() -> sc_executor::NativeVersion { diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs new file mode 100644 index 000000000000..aa0cae843c37 --- /dev/null +++ b/templates/parachain/runtime/src/apis.rs @@ -0,0 +1,275 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +// External crates imports +use frame_support::{ + genesis_builder_helper::{build_config, create_default_config}, + weights::Weight, +}; +use pallet_aura::Authorities; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +use sp_std::prelude::Vec; +use sp_version::RuntimeVersion; + +// Local module imports +use super::{ + AccountId, Aura, Balance, Block, Executive, InherentDataExt, Nonce, ParachainSystem, Runtime, + RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION, +}; + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Authorities::::get().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Nonce { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + use super::RuntimeBlockWeights; + + let weight = Executive::try_runtime_upgrade(checks).unwrap(); + (weight, RuntimeBlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + signature_check: bool, + select: frame_try_runtime::TryStateSelect, + ) -> Weight { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. + Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use frame_system_benchmarking::Pallet as SystemBench; + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + use super::*; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{BenchmarkError, Benchmarking, BenchmarkBatch}; + use super::*; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime { + fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { + ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); + Ok(()) + } + + fn verify_set_code() { + System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); + } + } + + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + impl cumulus_pallet_session_benchmarking::Config for Runtime {} + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + build_config::(config) + } + } +} diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index ad21b79a5b1b..5cfee123b01b 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -6,19 +6,17 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod apis; mod weights; pub mod xcm_config; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use smallvec::smallvec; -use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, }; use sp_std::prelude::*; @@ -30,7 +28,6 @@ use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, - genesis_builder_helper::{build_config, create_default_config}, parameter_types, traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, weights::{ @@ -57,8 +54,6 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use pallet_aura::Authorities; - // XCM Imports use xcm::latest::prelude::BodyId; @@ -187,7 +182,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { authoring_version: 1, spec_version: 1, impl_version: 0, - apis: RUNTIME_API_VERSIONS, + apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, state_version: 1, }; @@ -414,6 +409,7 @@ impl pallet_message_queue::Config for Runtime { type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; type MaxStale = sp_core::ConstU32<8>; type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); } impl cumulus_pallet_aura_ext::Config for Runtime {} @@ -542,230 +538,6 @@ mod benches { ); } -impl_runtime_apis! { - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) - } - - fn authorities() -> Vec { - Authorities::::get().into_inner() - } - } - - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block) - } - - fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - Executive::initialize_block(header) - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - OpaqueMetadata::new(Runtime::metadata().into()) - } - - fn metadata_at_version(version: u32) -> Option { - Runtime::metadata_at_version(version) - } - - fn metadata_versions() -> sp_std::vec::Vec { - Runtime::metadata_versions() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { - Executive::apply_extrinsic(extrinsic) - } - - fn finalize_block() -> ::Header { - Executive::finalize_block() - } - - fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { - data.create_extrinsics() - } - - fn check_inherents( - block: Block, - data: sp_inherents::InherentData, - ) -> sp_inherents::CheckInherentsResult { - data.check_extrinsics(&block) - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - source: TransactionSource, - tx: ::Extrinsic, - block_hash: ::Hash, - ) -> TransactionValidity { - Executive::validate_transaction(source, tx, block_hash) - } - } - - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - Executive::offchain_worker(header) - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) - } - - fn decode_session_keys( - encoded: Vec, - ) -> Option, KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(account: AccountId) -> Nonce { - System::account_nonce(account) - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { - fn query_info( - uxt: ::Extrinsic, - len: u32, - ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { - TransactionPayment::query_info(uxt, len) - } - fn query_fee_details( - uxt: ::Extrinsic, - len: u32, - ) -> pallet_transaction_payment::FeeDetails { - TransactionPayment::query_fee_details(uxt, len) - } - fn query_weight_to_fee(weight: Weight) -> Balance { - TransactionPayment::weight_to_fee(weight) - } - fn query_length_to_fee(length: u32) -> Balance { - TransactionPayment::length_to_fee(length) - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi - for Runtime - { - fn query_call_info( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::RuntimeDispatchInfo { - TransactionPayment::query_call_info(call, len) - } - fn query_call_fee_details( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::FeeDetails { - TransactionPayment::query_call_fee_details(call, len) - } - fn query_weight_to_fee(weight: Weight) -> Balance { - TransactionPayment::weight_to_fee(weight) - } - fn query_length_to_fee(length: u32) -> Balance { - TransactionPayment::length_to_fee(length) - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { - ParachainSystem::collect_collation_info(header) - } - } - - #[cfg(feature = "try-runtime")] - impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { - let weight = Executive::try_runtime_upgrade(checks).unwrap(); - (weight, RuntimeBlockWeights::get().max_block) - } - - fn execute_block( - block: Block, - state_root_check: bool, - signature_check: bool, - select: frame_try_runtime::TryStateSelect, - ) -> Weight { - // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to - // have a backtrace here. - Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark for Runtime { - fn benchmark_metadata(extra: bool) -> ( - Vec, - Vec, - ) { - use frame_benchmarking::{Benchmarking, BenchmarkList}; - use frame_support::traits::StorageInfoTrait; - use frame_system_benchmarking::Pallet as SystemBench; - use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - - let mut list = Vec::::new(); - list_benchmarks!(list, extra); - - let storage_info = AllPalletsWithSystem::storage_info(); - (list, storage_info) - } - - fn dispatch_benchmark( - config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { - use frame_benchmarking::{BenchmarkError, Benchmarking, BenchmarkBatch}; - - use frame_system_benchmarking::Pallet as SystemBench; - impl frame_system_benchmarking::Config for Runtime { - fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { - ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); - Ok(()) - } - - fn verify_set_code() { - System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); - } - } - - use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} - - use frame_support::traits::WhitelistedStorageKeys; - let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); - - let mut batches = Vec::::new(); - let params = (&config, &whitelist); - add_benchmarks!(params, batches); - - if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } - Ok(batches) - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn create_default_config() -> Vec { - create_default_config::() - } - - fn build_config(config: Vec) -> sp_genesis_builder::Result { - build_config::(config) - } - } -} - cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, From c7855fbffb8c60901ab8ed3f846dc38df660b18a Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 28 Mar 2024 13:16:37 +0100 Subject: [PATCH 195/202] add virtual nominator storage --- substrate/frame/staking/src/pallet/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 6b0df8ffcb91..3b7279673d50 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -387,6 +387,16 @@ pub mod pallet { pub type Nominators = CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations>; + /// Nominators whose funds are managed by other pallets. + /// + /// This pallet does not apply any locks on them, hence they are only virtually bonded. These + /// nominators should not be allowed to mutate their ledger directly via application code and + /// can only be accessed via low level functions made available by this pallet. + // TODO(ank4n): Can we keep this entry in `Ledger`? + #[pallet::storage] + pub type VirtualNominators = + CountedStorageMap<_, Twox64Concat, T::AccountId, ()>; + /// The maximum nominator count before we stop allowing new validators to join. /// /// When this value is not set, no limits are enforced. From 71e7947a5a9862f0291d4f9e27b4af4e1be5889a Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 04:42:34 +0100 Subject: [PATCH 196/202] add StakingUnsafe --- .../frame/delegated-staking/src/impls.rs | 4 ---- substrate/frame/delegated-staking/src/lib.rs | 8 ++++--- substrate/frame/nomination-pools/src/mock.rs | 4 ---- substrate/frame/staking/src/ledger.rs | 4 ++-- substrate/frame/staking/src/pallet/impls.rs | 14 +++++++++-- substrate/frame/staking/src/pallet/mod.rs | 3 +-- substrate/primitives/staking/src/lib.rs | 23 +++++++++++++++---- 7 files changed, 38 insertions(+), 22 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index caf6b9aa4281..3350e8b1aa3e 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -165,10 +165,6 @@ impl StakingInterface for Pallet { T::CoreStaking::slash_reward_fraction() } - fn unsafe_release_all(_who: &Self::AccountId) { - defensive_assert!(false, "unsafe_release_all is not supported"); - } - #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> sp_staking::Page { T::CoreStaking::max_exposure_page_size() diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 60eabb005c99..dcc5ca3e8616 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -179,7 +179,9 @@ use sp_runtime::{ traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; -use sp_staking::{delegation::DelegateeSupport, EraIndex, Stake, StakerStatus, StakingInterface}; +use sp_staking::{ + delegation::DelegateeSupport, EraIndex, Stake, StakerStatus, StakingInterface, StakingUnsafe, +}; use sp_std::{convert::TryInto, prelude::*}; pub type BalanceOf = @@ -215,7 +217,7 @@ pub mod pallet { type RuntimeHoldReason: From; /// Core staking implementation. - type CoreStaking: StakingInterface, AccountId = Self::AccountId>; + type CoreStaking: StakingUnsafe, AccountId = Self::AccountId>; } #[pallet::error] @@ -519,7 +521,7 @@ impl Pallet { let stake = T::CoreStaking::stake(who)?; // release funds from core staking. - T::CoreStaking::unsafe_release_all(who); + T::CoreStaking::force_release(who); // transferring just released staked amount. This should never fail but if it does, it // indicates bad state and we abort. diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 697a067cf4de..5e2303284bfd 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -224,10 +224,6 @@ impl sp_staking::StakingInterface for StakingMock { fn slash_reward_fraction() -> Perbill { unimplemented!("method currently not used in testing") } - - fn unsafe_release_all(_who: &Self::AccountId) { - unimplemented!("method currently not used in testing") - } } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index e91a398819eb..abbc5d4440ac 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -32,7 +32,7 @@ //! state consistency. use frame_support::{defensive, ensure, traits::Defensive}; -use sp_staking::{StakingAccount, StakingInterface}; +use sp_staking::{StakingAccount, StakingUnsafe}; use sp_std::prelude::*; use crate::{ @@ -252,7 +252,7 @@ impl StakingLedger { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { - Pallet::::unsafe_release_all(&ledger.stash); + Pallet::::force_release(&ledger.stash); Ledger::::remove(controller); >::remove(&stash); diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 887af37ae638..ad852b20445e 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -44,7 +44,7 @@ use sp_staking::{ offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, StakingAccount::{self, Controller, Stash}, - StakingInterface, + StakingInterface, StakingUnsafe, }; use sp_std::prelude::*; @@ -1899,10 +1899,20 @@ impl StakingInterface for Pallet { fn slash_reward_fraction() -> Perbill { SlashRewardFraction::::get() } +} - fn unsafe_release_all(who: &Self::AccountId) { +impl StakingUnsafe for Pallet { + fn force_release(who: &Self::AccountId) { T::Currency::remove_lock(crate::STAKING_ID, who) } + + fn virtual_bond( + who: &Self::AccountId, + value: Self::Balance, + payee: &Self::AccountId, + ) -> DispatchResult { + unimplemented!() + } } /// Standard implementation of `DelegateeSupport` that supports only direct staking and no diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 3b7279673d50..743febd408b8 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -394,8 +394,7 @@ pub mod pallet { /// can only be accessed via low level functions made available by this pallet. // TODO(ank4n): Can we keep this entry in `Ledger`? #[pallet::storage] - pub type VirtualNominators = - CountedStorageMap<_, Twox64Concat, T::AccountId, ()>; + pub type VirtualNominators = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>; /// The maximum nominator count before we stop allowing new validators to join. /// diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 65326b7e36a9..6ea45e2f3e64 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -295,11 +295,6 @@ pub trait StakingInterface { /// Returns the fraction of the slash to be rewarded to reporter. fn slash_reward_fraction() -> Perbill; - /// Release all funds bonded for stake. - /// - /// Unsafe, only used for migration of `delegatee` accounts. - fn unsafe_release_all(who: &Self::AccountId); - #[cfg(feature = "runtime-benchmarks")] fn max_exposure_page_size() -> Page; @@ -314,6 +309,24 @@ pub trait StakingInterface { fn set_current_era(era: EraIndex); } +/// Set of low level apis to manipulate staking ledger. +/// +/// This is a low level api and should be used if you know what you are doing. +pub trait StakingUnsafe: StakingInterface { + /// Release all funds bonded for stake without unbonding the ledger. + /// + /// Unsafe, only used for migration of `nominator` to `virtual_nominator`. + fn force_release(who: &Self::AccountId); + + /// Book-keep a new bond for `who` without applying any locks (hence virtual). + /// + /// Caller is responsible for ensuring the passed amount is locked and valid. + fn virtual_bond( + who: &Self::AccountId, + value: Self::Balance, + payee: &Self::AccountId, + ) -> DispatchResult; +} /// The amount of exposure for an era that an individual nominator has (susceptible to slashing). #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct IndividualExposure { From 9ad65b37f39f033e9cfdf7e74e794a07a40e17cb Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 05:53:07 +0100 Subject: [PATCH 197/202] impl bond_extra --- .../frame/delegated-staking/src/impls.rs | 2 +- substrate/frame/delegated-staking/src/lib.rs | 2 +- substrate/frame/staking/src/ledger.rs | 6 +- substrate/frame/staking/src/lib.rs | 15 ---- substrate/frame/staking/src/pallet/impls.rs | 85 ++++++++++++++++--- substrate/frame/staking/src/pallet/mod.rs | 25 +----- substrate/frame/staking/src/slashing.rs | 2 +- substrate/primitives/staking/src/lib.rs | 3 +- 8 files changed, 81 insertions(+), 59 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 3350e8b1aa3e..610a0a33142d 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -94,7 +94,7 @@ impl StakingInterface for Pallet { ensure!(delegatee.available_to_bond() >= value, Error::::NotEnoughFunds); ensure!(delegatee.ledger.payee == *payee, Error::::InvalidRewardDestination); - T::CoreStaking::bond(who, value, payee) + T::CoreStaking::virtual_bond(who, value, payee) } fn nominate(who: &Self::AccountId, validators: Vec) -> DispatchResult { diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index dcc5ca3e8616..618c20c2f976 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -544,7 +544,7 @@ impl Pallet { if delegatee.is_bonded() { T::CoreStaking::bond_extra(&delegatee.key, amount) } else { - T::CoreStaking::bond(&delegatee.key, amount, &delegatee.reward_account()) + T::CoreStaking::virtual_bond(&delegatee.key, amount, &delegatee.reward_account()) } } diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index abbc5d4440ac..a0907f77e014 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -178,7 +178,7 @@ impl StakingLedger { return Err(Error::::NotStash) } - Pallet::::update_hold(&self.stash, self.total).map_err(|_| Error::::BadState)?; + Pallet::::update_lock(&self.stash, self.total).map_err(|_| Error::::BadState)?; Ledger::::insert( &self.controller().ok_or_else(|| { @@ -199,7 +199,7 @@ impl StakingLedger { return Err(Error::::AlreadyBonded); } - if Pallet::::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { + if Pallet::::restrict_reward_destination(&self.stash, payee.clone()) { return Err(Error::::RewardDestinationRestricted); } @@ -214,7 +214,7 @@ impl StakingLedger { return Err(Error::::NotStash); } - if Pallet::::restrict_reward_destination(&self.stash, payee.clone().from(&self.stash)) { + if Pallet::::restrict_reward_destination(&self.stash, payee.clone()) { return Err(Error::::RewardDestinationRestricted); } diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 9a4386086aa6..2e69881dbee7 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -413,21 +413,6 @@ pub enum RewardDestination { None, } -impl RewardDestination { - fn from(self, stash: &AccountId) -> Option { - match self { - // FIXME(ank4n): Figure out later how to handle Controller - RewardDestination::Staked | RewardDestination::Stash => Some(stash.clone()), - RewardDestination::Account(a) => Some(a), - #[allow(deprecated)] - _ => { - defensive!("reward destination not set or set as deprecated controller"); - None - }, - } - } -} - /// Preference of what happens regarding validation. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default, MaxEncodedLen)] pub struct ValidatorPrefs { diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index ad852b20445e..1c1ad3820b73 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -35,7 +35,9 @@ use frame_support::{ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use pallet_session::historical; use sp_runtime::{ - traits::{Bounded, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero}, + traits::{ + Bounded, CheckedSub, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero, + }, Perbill, Percent, }; use sp_staking::{ @@ -151,6 +153,37 @@ impl Pallet { Self::slashable_balance_of_vote_weight(who, issuance) } + pub(super) fn do_bond_extra(stash: &T::AccountId, additional: BalanceOf) -> DispatchResult { + let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; + + let extra = if Self::is_virtual_nominator(stash) { + additional + } else { + // additional amount or actual balance of stash whichever is lower. + additional.min( + Self::stakeable_balance(stash) + .checked_sub(&ledger.total) + .ok_or(sp_runtime::ArithmeticError::Overflow)?, + ) + }; + + ledger.total += extra; + ledger.active += extra; + // Last check: the new active amount of ledger must be more than ED. + ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientBond); + + // NOTE: ledger must be updated prior to calling `Self::weight_of`. + ledger.update()?; + // update this staker in the sorted list, if they exist in it. + if T::VoterList::contains(stash) { + let _ = T::VoterList::on_update(&stash, Self::weight_of(stash)).defensive(); + } + + Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: extra }); + + Ok(()) + } + pub(super) fn do_withdraw_unbonded( controller: &T::AccountId, num_slashing_spans: u32, @@ -1139,31 +1172,39 @@ impl Pallet { EraInfo::::get_full_exposure(era, account) } + /// Balance that can be staked in the pallet. Includes already staked balance. pub(crate) fn stakeable_balance(who: &T::AccountId) -> BalanceOf { - if T::DelegateeSupport::is_delegatee(who) { - return T::DelegateeSupport::stakeable_balance(who); - } - T::Currency::free_balance(who) } + /// Whether the passed reward destination is restricted for the given account. + /// + /// Virtual nominators are not allowed to compound their rewards as this pallet does not manage + /// locks for them. For external pallets that manage the virtual bond, it is their + /// responsibility to distribute the reward and re-bond them. + /// + /// Conservatively, we expect them to always set the reward destination to a non stash account. pub(crate) fn restrict_reward_destination( who: &T::AccountId, - reward_destination: Option, + reward_destination: RewardDestination, ) -> bool { - if T::DelegateeSupport::is_delegatee(who) { - return T::DelegateeSupport::restrict_reward_destination(who, reward_destination); - } + Self::is_virtual_nominator(who) && + match reward_destination { + RewardDestination::Account(payee) => payee == *who, + _ => true, + } + } - false + pub(crate) fn is_virtual_nominator(who: &T::AccountId) -> bool { + VirtualNominators::::contains_key(who) } - pub(crate) fn update_hold( + pub(crate) fn update_lock( who: &T::AccountId, amount: BalanceOf, ) -> sp_runtime::DispatchResult { - // only apply lock if it is not a delegatee. delegatee accounts are already locked/held. - if !T::DelegateeSupport::is_delegatee(who) { + // Skip locking virtual nominators. They are handled by external pallets. + if !Self::is_virtual_nominator(who) { T::Currency::set_lock(crate::STAKING_ID, who, amount, WithdrawReasons::all()); } @@ -1911,7 +1952,23 @@ impl StakingUnsafe for Pallet { value: Self::Balance, payee: &Self::AccountId, ) -> DispatchResult { - unimplemented!() + if StakingLedger::::is_bonded(StakingAccount::Stash(who.clone())) { + return Err(Error::::AlreadyBonded.into()) + } + + frame_system::Pallet::::inc_consumers(&who).map_err(|_| Error::::BadState)?; + + // mark who as a virtual nominator + VirtualNominators::::insert(who, ()); + + Self::deposit_event(Event::::Bonded { stash: who.clone(), amount: value }); + let ledger = StakingLedger::::new(who.clone(), value); + + // You're auto-bonded forever, here. We might improve this by only bonding when + // you actually validate/nominate and remove once you unbond __everything__. + ledger.bond(RewardDestination::Account(payee.clone()))?; + + Ok(()) } } diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 743febd408b8..b51ed34065e9 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -32,7 +32,7 @@ use frame_support::{ }; use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; use sp_runtime::{ - traits::{CheckedSub, SaturatedConversion, StaticLookup, Zero}, + traits::{SaturatedConversion, StaticLookup, Zero}, ArithmeticError, Perbill, Percent, }; @@ -1005,29 +1005,8 @@ pub mod pallet { #[pallet::compact] max_additional: BalanceOf, ) -> DispatchResult { let stash = ensure_signed(origin)?; - let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; - let stash_balance = Self::stakeable_balance(&stash); - if let Some(extra) = stash_balance.checked_sub(&ledger.total) { - let extra = extra.min(max_additional); - ledger.total += extra; - ledger.active += extra; - // Last check: the new active amount of ledger must be more than ED. - ensure!( - ledger.active >= T::Currency::minimum_balance(), - Error::::InsufficientBond - ); - - // NOTE: ledger must be updated prior to calling `Self::weight_of`. - ledger.update()?; - // update this staker in the sorted list, if they exist in it. - if T::VoterList::contains(&stash) { - let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash)).defensive(); - } - - Self::deposit_event(Event::::Bonded { stash, amount: extra }); - } - Ok(()) + Self::do_bond_extra(&stash, max_additional) } /// Schedule a portion of the stash to be unlocked ready for transfer out after the bond diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index 364dc4215dfc..e42165299b4f 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -608,7 +608,7 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; - let lazy_slash = T::DelegateeSupport::is_delegatee(stash); + let lazy_slash = Pallet::::is_virtual_nominator(stash); let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); if value.is_zero() { diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 6ea45e2f3e64..b5188717f8ce 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -311,7 +311,7 @@ pub trait StakingInterface { /// Set of low level apis to manipulate staking ledger. /// -/// This is a low level api and should be used if you know what you are doing. +/// These apis bypass safety checks and should only be used if you know what you are doing. pub trait StakingUnsafe: StakingInterface { /// Release all funds bonded for stake without unbonding the ledger. /// @@ -327,6 +327,7 @@ pub trait StakingUnsafe: StakingInterface { payee: &Self::AccountId, ) -> DispatchResult; } + /// The amount of exposure for an era that an individual nominator has (susceptible to slashing). #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct IndividualExposure { From d884c47b446962068ee3bc3058a80e18dc64f693 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 06:04:29 +0100 Subject: [PATCH 198/202] report slash via event listener --- substrate/frame/delegated-staking/src/impls.rs | 16 ++++++++++++++-- substrate/frame/delegated-staking/src/mock.rs | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 610a0a33142d..d0e5fb5da497 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -305,10 +305,22 @@ impl DelegateeSupport for Pallet { } fn report_slash(who: &Self::AccountId, slash: Self::Balance) { + + } +} + +impl sp_staking::OnStakingUpdate> for Pallet { + fn on_slash( + who: &T::AccountId, + _slashed_active: BalanceOf, + _slashed_unlocking: &sp_std::collections::btree_map::BTreeMap>, + slashed_total: BalanceOf, + ) { >::mutate(who, |maybe_register| match maybe_register { - Some(register) => register.pending_slash.saturating_accrue(slash), + // if delegatee, register the slashed amount as pending slash. + Some(register) => register.pending_slash.saturating_accrue(slashed_total), None => { - defensive!("should not be called on non-delegate"); + // nothing to do }, }); } diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 892b69b624e4..09f12dd243f2 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -133,7 +133,7 @@ impl pallet_staking::Config for Runtime { type NominationsQuota = pallet_staking::FixedNominationsQuota<16>; type MaxUnlockingChunks = ConstU32<32>; type MaxControllersInDeprecationBatch = ConstU32<100>; - type EventListeners = Pools; + type EventListeners = (Pools, DelegatedStaking); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } From 500ac8fb99173b9dcba1f094db2c05ab6c7338d7 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 06:15:00 +0100 Subject: [PATCH 199/202] get rid of delegatee support --- .../frame/delegated-staking/src/impls.rs | 51 ++----------------- substrate/frame/delegated-staking/src/lib.rs | 16 +++--- substrate/frame/delegated-staking/src/mock.rs | 3 +- .../frame/delegated-staking/src/tests.rs | 1 - .../nomination-pools/test-staking/src/mock.rs | 1 - substrate/frame/staking/src/lib.rs | 2 +- substrate/frame/staking/src/mock.rs | 1 - substrate/frame/staking/src/pallet/impls.rs | 19 ------- substrate/frame/staking/src/pallet/mod.rs | 7 --- substrate/frame/staking/src/slashing.rs | 9 ++-- .../primitives/staking/src/delegation.rs | 49 ------------------ 11 files changed, 18 insertions(+), 141 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index d0e5fb5da497..834bba4f29d6 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -16,10 +16,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Implementations of public traits, namely [StakingInterface], and [DelegateeSupport]. +//! Implementations of public traits, namely [StakingInterface], [DelegatedStakeInterface] and +//! [OnStakingUpdate]. use super::*; -use sp_staking::delegation::DelegatedStakeInterface; +use sp_staking::{delegation::DelegatedStakeInterface, OnStakingUpdate}; /// StakingInterface implementation with delegation support. /// @@ -265,51 +266,7 @@ impl DelegatedStakeInterface for Pallet { } } -impl DelegateeSupport for Pallet { - type Balance = BalanceOf; - type AccountId = T::AccountId; - - /// this balance is total delegator that can be staked, and importantly not extra balance that - /// is delegated but not bonded yet. - fn stakeable_balance(who: &Self::AccountId) -> Self::Balance { - Delegatee::::from(who) - .map(|delegatee| delegatee.ledger.stakeable_balance()) - .unwrap_or_default() - } - - fn restrict_reward_destination( - who: &Self::AccountId, - reward_destination: Option, - ) -> bool { - let maybe_register = >::get(who); - - if maybe_register.is_none() { - // no restrictions for non delegates. - return false; - } - - // restrict if reward destination is not set - if reward_destination.is_none() { - return true; - } - - let register = maybe_register.expect("checked above; qed"); - let reward_acc = reward_destination.expect("checked above; qed"); - - // restrict if reward account is not what delegate registered. - register.payee != reward_acc - } - - fn is_delegatee(who: &Self::AccountId) -> bool { - Self::is_delegatee(who) - } - - fn report_slash(who: &Self::AccountId, slash: Self::Balance) { - - } -} - -impl sp_staking::OnStakingUpdate> for Pallet { +impl OnStakingUpdate> for Pallet { fn on_slash( who: &T::AccountId, _slashed_active: BalanceOf, diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 618c20c2f976..dd0a29cd7830 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -83,10 +83,6 @@ //! [Config::CoreStaking] to provide delegation based staking. NominationPool can use this pallet as //! its Staking provider to support delegation based staking from pool accounts. //! -//! #### [Delegatee Support](DelegateeSupport) -//! Implements `DelegateeSupport` trait which an implementation of [StakingInterface] (such as -//! pallet-staking) can use to back-support `delegatee` accounts. -//! //! ## Lazy Slashing //! One of the reasons why direct nominators on staking pallet cannot scale well is because all //! nominators are slashed at the same time. This is expensive and needs to be bounded operation. @@ -179,9 +175,7 @@ use sp_runtime::{ traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; -use sp_staking::{ - delegation::DelegateeSupport, EraIndex, Stake, StakerStatus, StakingInterface, StakingUnsafe, -}; +use sp_staking::{EraIndex, Stake, StakerStatus, StakingInterface, StakingUnsafe}; use sp_std::{convert::TryInto, prelude::*}; pub type BalanceOf = @@ -762,6 +756,14 @@ impl Pallet { Ok(()) } + + /// Total balance that is available for stake. Includes already staked amount. + #[cfg(test)] + pub(crate) fn stakeable_balance(who: &T::AccountId) -> BalanceOf { + Delegatee::::from(who) + .map(|delegatee| delegatee.ledger.stakeable_balance()) + .unwrap_or_default() + } } #[cfg(any(test, feature = "try-runtime"))] diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 09f12dd243f2..ae1b84acdecb 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -34,7 +34,7 @@ use frame_support::dispatch::RawOrigin; use pallet_staking::CurrentEra; use sp_core::U256; use sp_runtime::traits::Convert; -use sp_staking::{delegation::DelegateeSupport, Stake, StakingInterface}; +use sp_staking::{Stake, StakingInterface}; pub type T = Runtime; type Block = frame_system::mocking::MockBlock; @@ -109,7 +109,6 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; - type DelegateeSupport = DelegatedStaking; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index a278fd8850ad..6e6fa6fb9169 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -22,7 +22,6 @@ use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold}; use pallet_nomination_pools::{Error as PoolsError, Event as PoolsEvent}; use pallet_staking::Error as StakingError; -use sp_staking::delegation::DelegateeSupport; #[test] fn create_a_delegatee_with_first_delegator() { diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index 36d936de31a9..227f5ef5d596 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -111,7 +111,6 @@ parameter_types! { impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; - type DelegateeSupport = pallet_staking::NoDelegation; type UnixTime = pallet_timestamp::Pallet; type CurrencyToVote = (); type RewardRemainder = (); diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 2e69881dbee7..aeae8e80fb83 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -328,7 +328,7 @@ pub use sp_staking::{Exposure, IndividualExposure, StakerStatus}; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; pub use weights::WeightInfo; -pub use pallet::{pallet::*, NoDelegation, UseNominatorsAndValidatorsMap, UseValidatorsMap}; +pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap}; pub(crate) const STAKING_ID: LockIdentifier = *b"staking "; pub(crate) const LOG_TARGET: &str = "runtime::staking"; diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index ae55b2afabbb..6db462c1a70f 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -266,7 +266,6 @@ impl OnStakingUpdate for EventListenerMock { impl crate::pallet::pallet::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; - type DelegateeSupport = pallet_staking::NoDelegation; type UnixTime = Timestamp; type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 1c1ad3820b73..d67d8cc24e9d 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -42,7 +42,6 @@ use sp_runtime::{ }; use sp_staking::{ currency_to_vote::CurrencyToVote, - delegation::DelegateeSupport, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, StakingAccount::{self, Controller, Stash}, @@ -1972,24 +1971,6 @@ impl StakingUnsafe for Pallet { } } -/// Standard implementation of `DelegateeSupport` that supports only direct staking and no -/// delegated staking. -pub struct NoDelegation(PhantomData); -impl DelegateeSupport for NoDelegation { - type Balance = BalanceOf; - type AccountId = T::AccountId; - fn stakeable_balance(_who: &Self::AccountId) -> Self::Balance { - defensive!("stakeable balance should not have been called for NoDelegation"); - BalanceOf::::zero() - } - fn is_delegatee(_who: &Self::AccountId) -> bool { - false - } - fn report_slash(_who: &Self::AccountId, _slash: Self::Balance) { - defensive!("delegation report_slash should not be have been called for NoDelegation"); - } -} - #[cfg(any(test, feature = "try-runtime"))] impl Pallet { pub(crate) fn do_try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index b51ed34065e9..6d5b64f47360 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -37,7 +37,6 @@ use sp_runtime::{ }; use sp_staking::{ - delegation::DelegateeSupport, EraIndex, Page, SessionIndex, StakingAccount::{self, Controller, Stash}, }; @@ -105,12 +104,6 @@ pub mod pallet { + TypeInfo + MaxEncodedLen; - /// Something that provides delegation support to staking pallet. - type DelegateeSupport: DelegateeSupport< - Balance = Self::CurrencyBalance, - AccountId = Self::AccountId, - >; - /// Time used for computing era duration. /// /// It is guaranteed to start being called from the first `on_finalize`. Thus value at diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index e42165299b4f..3345031a36ca 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -64,7 +64,7 @@ use sp_runtime::{ traits::{Saturating, Zero}, DispatchResult, RuntimeDebug, }; -use sp_staking::{delegation::DelegateeSupport, offence::DisableStrategy, EraIndex}; +use sp_staking::{offence::DisableStrategy, EraIndex}; use sp_std::vec::Vec; /// The proportion of the slashing reward to be paid out on the first slashing detection. @@ -608,7 +608,6 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; - let lazy_slash = Pallet::::is_virtual_nominator(stash); let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); if value.is_zero() { @@ -616,10 +615,8 @@ pub fn do_slash( return } - if lazy_slash { - // If delegated staking, report slash and move on. - T::DelegateeSupport::report_slash(stash, value); - } else { + // Skip slashing for virtual nominators. The pallets managing them should handle the slashing. + if !Pallet::::is_virtual_nominator(stash) { let (imbalance, missing) = T::Currency::slash(stash, value); slashed_imbalance.subsume(imbalance); diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index e51a48fddf3a..ba30cb58fcba 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -21,55 +21,6 @@ use scale_info::TypeInfo; use sp_runtime::{DispatchResult, Saturating}; use sp_std::ops::Sub; -/// Support plugin for `delegatee` accounts. -/// -/// A `delegatee` account is an account that can receive delegations from other accounts. Their -/// balance is made up of multiple child delegators. This trait allows a pallet such as -/// `pallet-staking` to support these special accounts. -pub trait DelegateeSupport { - /// Balance type used by the staking system. - type Balance: Sub - + Ord - + PartialEq - + Default - + Copy - + MaxEncodedLen - + FullCodec - + TypeInfo - + Saturating; - - /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; - - /// Balance of `delegatee` which can be staked. - /// - /// Similar to free balance for a normal account. - fn stakeable_balance(delegatee: &Self::AccountId) -> Self::Balance; - - /// Returns true if `delegatee` is restricted to update which account they can receive their - /// staking rewards. - /// - /// For `delegatee` accounts we restrict the reward destination to be the same as the - /// `delegatee` account itself. This is since the actual `delegatee` balances is not considered - /// while staking. Instead, their balance is made up of multiple child delegators. - fn restrict_reward_destination( - _who: &Self::AccountId, - _reward_destination: Option, - ) -> bool { - // never restrict by default - false - } - - /// Returns true if `who` is a `delegatee` and accepts delegations from other accounts. - fn is_delegatee(who: &Self::AccountId) -> bool; - - /// Reports an ongoing slash to the `delegatee` account that would be applied lazily. - /// - /// Slashing a delegatee account is not immediate since the balance is made up of multiple child - /// delegators. This function should bookkeep the slash to be applied later. - fn report_slash(who: &Self::AccountId, slash: Self::Balance); -} - /// Trait that extends on [`StakingInterface`] to provide additional capability to delegate funds to /// an account. pub trait DelegatedStakeInterface: StakingInterface { From a8fc38505ea51f210e6629090696bdc9f4ea3e28 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 07:38:20 +0100 Subject: [PATCH 200/202] fix tests --- substrate/frame/delegated-staking/src/lib.rs | 6 ------ substrate/frame/delegated-staking/src/tests.rs | 8 +------- substrate/frame/staking/src/pallet/mod.rs | 7 ++++--- substrate/primitives/staking/src/delegation.rs | 5 +---- substrate/primitives/staking/src/lib.rs | 5 +++-- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index dd0a29cd7830..b50769f88ffc 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -100,12 +100,6 @@ //! ## Migration from Nominator to Delegatee //! More details [here](https://hackmd.io/@ak0n/np-delegated-staking-migration). //! -//! ## Reward Destination Restrictions -//! This pallets set an important restriction of rewards account to be separate from `delegatee` -//! account. This is because, `delegatee` balance is not what is directly exposed but the funds that -//! are delegated to it. For `delegatee` accounts, we have also no way to auto-compound rewards. The -//! rewards need to be paid out to delegators and then delegated again to the `delegatee` account. -//! //! ## Nomination Pool vs Delegation Staking //! This pallet is not a replacement for Nomination Pool but adds a new primitive over staking //! pallet that can be used by Nomination Pool to support delegation based staking. It can be diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 6e6fa6fb9169..1fa873f9ecae 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -403,13 +403,7 @@ mod staking_integration { Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Stash), StakingError::::RewardDestinationRestricted ); - - // non stash account different than one passed to DelegatedStaking also does not work.. - assert_noop!( - Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Account(202)), - StakingError::::RewardDestinationRestricted - ); - + // passing correct reward destination works assert_ok!(Staking::set_payee( RuntimeOrigin::signed(200), diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 6d5b64f47360..5bb7efe31acb 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -382,9 +382,10 @@ pub mod pallet { /// Nominators whose funds are managed by other pallets. /// - /// This pallet does not apply any locks on them, hence they are only virtually bonded. These - /// nominators should not be allowed to mutate their ledger directly via application code and - /// can only be accessed via low level functions made available by this pallet. + /// This pallet does not apply any locks on them, therefore they are only virtually bonded. They + /// are expected to be keyless accounts and hence should not be allowed to mutate their ledger + /// directly via this pallet. Instead, these accounts are managed by other pallets and accessed + /// via low level apis. We keep track of them to do minimal integrity checks. // TODO(ank4n): Can we keep this entry in `Ledger`? #[pallet::storage] pub type VirtualNominators = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>; diff --git a/substrate/primitives/staking/src/delegation.rs b/substrate/primitives/staking/src/delegation.rs index ba30cb58fcba..e7cad230b270 100644 --- a/substrate/primitives/staking/src/delegation.rs +++ b/substrate/primitives/staking/src/delegation.rs @@ -16,10 +16,7 @@ // limitations under the License. use crate::StakingInterface; -use codec::{FullCodec, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime::{DispatchResult, Saturating}; -use sp_std::ops::Sub; +use sp_runtime::{DispatchResult}; /// Trait that extends on [`StakingInterface`] to provide additional capability to delegate funds to /// an account. diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index b5188717f8ce..f6101f81c45c 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -320,9 +320,10 @@ pub trait StakingUnsafe: StakingInterface { /// Book-keep a new bond for `who` without applying any locks (hence virtual). /// - /// Caller is responsible for ensuring the passed amount is locked and valid. + /// It is important that who is a keyless account and therefore cannot interact with staking + /// pallet directly. Caller is responsible for ensuring the passed amount is locked and valid. fn virtual_bond( - who: &Self::AccountId, + keyless_who: &Self::AccountId, value: Self::Balance, payee: &Self::AccountId, ) -> DispatchResult; From 81c0c78780262cc41c59c6d693fbcf7f667fda4a Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 07:59:14 +0100 Subject: [PATCH 201/202] add update payee to Staking Interface --- substrate/frame/delegated-staking/src/impls.rs | 4 ++++ substrate/frame/delegated-staking/src/lib.rs | 3 +-- substrate/frame/delegated-staking/src/tests.rs | 2 +- substrate/frame/nomination-pools/src/mock.rs | 4 ++++ substrate/frame/staking/src/pallet/impls.rs | 6 ++++++ substrate/primitives/staking/src/lib.rs | 3 +++ 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 834bba4f29d6..e256b917a94b 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -122,6 +122,10 @@ impl StakingInterface for Pallet { T::CoreStaking::unbond(stash, value) } + fn update_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult { + T::CoreStaking::update_payee(stash, reward_acc) + } + /// Withdraw unbonding funds until current era. /// /// Funds are moved to unclaimed_withdrawals register of the `DelegateeLedger`. diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index b50769f88ffc..65058c927e86 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -517,8 +517,7 @@ impl Pallet { .map_err(|_| Error::::BadState)?; Self::do_register_delegatee(who, reward_account); - // FIXME(ank4n) expose set payee in staking interface. - // T::CoreStaking::set_payee(who, reward_account) + T::CoreStaking::update_payee(who, reward_account)?; Self::do_delegate(&proxy_delegator, who, stake.total) } diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 1fa873f9ecae..34f8399f340d 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -403,7 +403,7 @@ mod staking_integration { Staking::set_payee(RuntimeOrigin::signed(200), RewardDestination::Stash), StakingError::::RewardDestinationRestricted ); - + // passing correct reward destination works assert_ok!(Staking::set_payee( RuntimeOrigin::signed(200), diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index 5e2303284bfd..6bac1d6c1428 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -190,6 +190,10 @@ impl sp_staking::StakingInterface for StakingMock { } } + fn update_payee(_: &Self::AccountId, _: &Self::AccountId) { + unimplemented!("method currently not used in testing") + } + fn election_ongoing() -> bool { unimplemented!("method currently not used in testing") } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index d67d8cc24e9d..027bfa6e66dc 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1825,6 +1825,12 @@ impl StakingInterface for Pallet { .map(|_| ()) } + fn update_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult { + // since controller is deprecated and this function is never used for old ledgers with + // distinct controllers, we can safely assume that stash is the controller. + Self::set_payee(RawOrigin::Signed(stash.clone()).into(), RewardDestination::Account(reward_acc.clone())) + } + fn chill(who: &Self::AccountId) -> DispatchResult { // defensive-only: any account bonded via this interface has the stash set as the // controller, but we have to be sure. Same comment anywhere else that we read this. diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index f6101f81c45c..2300ab062b37 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -256,6 +256,9 @@ pub trait StakingInterface { /// schedules have reached their unlocking era should allow more calls to this function. fn unbond(stash: &Self::AccountId, value: Self::Balance) -> DispatchResult; + /// Update the reward destination for the ledger associated with the stash. + fn update_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult; + /// Unlock any funds schedule to unlock before or at the current era. /// /// Returns whether the stash was killed because of this withdraw or not. From 5ebc35a0d21da6a82198c5a4d25e3af5aee4f966 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 29 Mar 2024 08:02:03 +0100 Subject: [PATCH 202/202] get rid of fixme --- substrate/frame/delegated-staking/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 65058c927e86..3b49ff477f27 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -676,10 +676,6 @@ impl Pallet { .defensive_ok_or(Error::::BadState)? .save_or_kill(source_delegator); - // FIXME(ank4n): If all funds are migrated from source, it can be cleaned up and ED returned - // to delegate or alternatively whoever cleans it up. This could be a permission-less - // extrinsic. - // release funds from source let released = T::Currency::release( &HoldReason::Delegating.into(),