diff --git a/pallets/runtime/common/src/runtime.rs b/pallets/runtime/common/src/runtime.rs index d2551df48e..76a5a4a86a 100644 --- a/pallets/runtime/common/src/runtime.rs +++ b/pallets/runtime/common/src/runtime.rs @@ -259,7 +259,7 @@ macro_rules! misc_pallet_impls { type SlashDeferDuration = SlashDeferDuration; type AdminOrigin = polymesh_primitives::EnsureRoot; type SessionInterface = Self; - type EraPayout = pallet_staking::ConvertCurve; + type EraPayout = pallet_staking::PolymeshConvertCurve; type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; @@ -275,6 +275,7 @@ macro_rules! misc_pallet_impls { type Call = RuntimeCall; type PalletsOrigin = OriginCaller; type RewardScheduler = Scheduler; + type Permissioned = Staking; } impl pallet_authority_discovery::Config for Runtime { diff --git a/pallets/runtime/develop/src/benchmarks/pallet_session.rs b/pallets/runtime/develop/src/benchmarks/pallet_session.rs index 7a4cfa13b5..b0a01519bc 100644 --- a/pallets/runtime/develop/src/benchmarks/pallet_session.rs +++ b/pallets/runtime/develop/src/benchmarks/pallet_session.rs @@ -69,8 +69,7 @@ impl ValidatorInfo { Some(balance), ) .unwrap() - .0 - .account(); + .0; let controller = pallet_staking::Pallet::::bonded(&stash).expect("not stash"); let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap(); diff --git a/pallets/runtime/tests/src/staking/mock.rs b/pallets/runtime/tests/src/staking/mock.rs index 879199a78c..1d234484ae 100644 --- a/pallets/runtime/tests/src/staking/mock.rs +++ b/pallets/runtime/tests/src/staking/mock.rs @@ -40,7 +40,7 @@ use sp_staking::{EraIndex, SessionIndex}; use pallet_balances::AccountData; use pallet_balances::BlockRewardConfig; use pallet_staking::types::SlashingSwitch; -use pallet_staking::{self as pallet_staking, *}; +use pallet_staking::*; use polymesh_primitives::asset::AssetId; use polymesh_primitives::constants::currency::POLY; use polymesh_primitives::identity_id::GenesisIdentityRecord; @@ -599,7 +599,7 @@ impl pallet_staking::Config for Test { type SlashDeferDuration = SlashDeferDuration; type AdminOrigin = frame_system::EnsureRoot; type SessionInterface = Self; - type EraPayout = ConvertCurve; + type EraPayout = PolymeshConvertCurve; type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; @@ -615,6 +615,7 @@ impl pallet_staking::Config for Test { type Call = RuntimeCall; type PalletsOrigin = OriginCaller; type RewardScheduler = Scheduler; + type Permissioned = Staking; } impl frame_system::offchain::SendTransactionTypes for Test diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml index d081526f64..9c03892561 100644 --- a/pallets/staking/Cargo.toml +++ b/pallets/staking/Cargo.toml @@ -60,7 +60,6 @@ std = [ "pallet-babe/std", "sp-arithmetic/std", "rand_chacha", - "frame-benchmarking", ] runtime-benchmarks = [ "frame-benchmarking", diff --git a/pallets/staking/src/benchmarking.rs b/pallets/staking/src/benchmarking.rs index 6e01352dfa..92f9972229 100644 --- a/pallets/staking/src/benchmarking.rs +++ b/pallets/staking/src/benchmarking.rs @@ -36,7 +36,7 @@ use sp_staking::SessionIndex; use sp_std::prelude::*; pub use frame_benchmarking::v1::{ - account, benchmarks, impl_benchmark_test_suite, whitelisted_caller, + account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, }; use frame_system::RawOrigin; @@ -50,27 +50,24 @@ type MaxNominators = <::BenchmarkingConfig as BenchmarkingConfig // Polymesh change // ----------------------------------------------------------------- -use pallet_identity::benchmarking::{User, UserBuilder}; +use pallet_identity::benchmarking::UserBuilder; use polymesh_primitives::identity_claim::ClaimType; use polymesh_primitives::{IdentityId, Permissions}; -use crate::types::SlashingSwitch; +use crate::types::{PermissionedStaking, SlashingSwitch}; // ----------------------------------------------------------------- // Polymesh change // ----------------------------------------------------------------- -macro_rules! whitelist_account { - ($acc:expr) => { - let x = $acc.account(); - frame_benchmarking::v1::whitelist_account!(x); - }; -} -fn add_permissioned_validator_(id: IdentityId, intended_count: Option) { +fn add_permissioned_validator_(stash: &T::AccountId) { Staking::::set_validator_count(RawOrigin::Root.into(), 10) .expect("Failed to set the validator count"); - Staking::::add_permissioned_validator(RawOrigin::Root.into(), id, intended_count) - .expect("Failed to add permissioned validator"); + T::Permissioned::permission_validator(stash); +} + +fn get_did(who: &T::AccountId) -> IdentityId { + pallet_identity::Pallet::::get_identity(who).expect("Failed to get identity id") } // ----------------------------------------------------------------- @@ -102,7 +99,7 @@ pub fn create_validator_with_nominators( dead: bool, destination: RewardDestination, balance: Option, -) -> Result<(User, Vec<(User, User)>), &'static str> +) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>), &'static str> where T: Config, { @@ -120,12 +117,12 @@ where }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); Staking::::set_validator_count(RawOrigin::Root.into(), 10)?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), v_stash.did(), Some(2))?; - Staking::::validate(v_controller.origin().into(), validator_prefs)?; - let stash_lookup = v_stash.lookup(); + T::Permissioned::permission_validator(&v_stash); + Staking::::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?; + let stash_lookup = T::Lookup::unlookup(v_stash.clone()); points_total += 10; - points_individual.push((v_stash.account().clone(), 10)); + points_individual.push((v_stash.clone(), 10)); let original_nominator_count = Nominators::::count(); let mut nominators = Vec::new(); @@ -139,7 +136,7 @@ where }; if i < n { Staking::::nominate( - RawOrigin::Signed(n_controller.account()).into(), + RawOrigin::Signed(n_controller.clone()).into(), vec![stash_lookup.clone()], )?; nominators.push((n_stash, n_controller)); @@ -153,8 +150,7 @@ where assert_eq!(new_validators.len(), 1); assert_eq!( - new_validators[0], - v_stash.account(), + new_validators[0], v_stash, "Our validator was not selected!" ); assert_ne!(Validators::::count(), 0); @@ -185,13 +181,13 @@ benchmarks! { bond { let stash = create_funded_user::("stash", USER_SEED, 100); let controller = create_funded_user::("controller", USER_SEED, 100); - let controller_lookup = controller.lookup(); + let controller_lookup = T::Lookup::unlookup(controller.clone()); let reward_destination = RewardDestination::Staked; whitelist_account!(stash); - }: _(stash.origin(), controller_lookup, 2_000_000u32.into(), reward_destination) + }: _(RawOrigin::Signed(stash.clone()), controller_lookup, 2_000_000u32.into(), reward_destination) verify { - assert!(Bonded::::contains_key(stash.account())); - assert!(Ledger::::contains_key(controller.account())); + assert!(Bonded::::contains_key(stash)); + assert!(Ledger::::contains_key(controller)); } bond_extra { @@ -206,12 +202,12 @@ benchmarks! { let (stash, controller) = create_stash_controller::(USER_SEED, 100, RewardDestination::Staked).unwrap(); let original_bonded = - Ledger::::get(controller.account()).map(|l| l.active).ok_or("ledger not created")?; + Ledger::::get(controller.clone()).map(|l| l.active).ok_or("ledger not created")?; whitelist_account!(stash); - }: _(stash.origin(), 2_000_000u32.into()) + }: _(RawOrigin::Signed(stash), 2_000_000u32.into()) verify { - let ledger = Ledger::::get(controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(controller).ok_or("ledger not created after")?; let new_bonded: BalanceOf = ledger.active; assert!(original_bonded < new_bonded); } @@ -228,12 +224,12 @@ benchmarks! { let (stash, controller) = create_stash_controller::(USER_SEED, 100, RewardDestination::Staked).unwrap(); let original_bonded = - Ledger::::get(controller.account()).map(|l| l.active).ok_or("ledger not created")?; + Ledger::::get(controller.clone()).map(|l| l.active).ok_or("ledger not created")?; whitelist_account!(controller); - }: _(controller.origin(), 20u32.into()) + }: _(RawOrigin::Signed(controller.clone()), 20u32.into()) verify { - let ledger = Ledger::::get(controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(controller).ok_or("ledger not created after")?; let new_bonded: BalanceOf = ledger.active; assert!(original_bonded > new_bonded); } @@ -247,16 +243,16 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - add_slashing_spans::(&stash.account(), s); - Staking::::unbond(controller.origin().into(), 50u32.into())?; + add_slashing_spans::(&stash, s); + Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), 50u32.into())?; CurrentEra::::put(EraIndex::max_value()); let original_total = - Ledger::::get(controller.account()).map(|l| l.total).ok_or("ledger not created before")?; + Ledger::::get(controller.clone()).map(|l| l.total).ok_or("ledger not created before")?; whitelist_account!(controller); - }: withdraw_unbonded(controller.origin(), s) + }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) verify { - let ledger = Ledger::::get(controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(controller).ok_or("ledger not created after")?; let new_total: BalanceOf = ledger.total; assert!(original_total > new_total); } @@ -274,16 +270,16 @@ benchmarks! { // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - add_slashing_spans::(&stash.account(), s); - Staking::::unbond(controller.origin().into(), 10_000_000u32.into())?; + add_slashing_spans::(&stash, s); + Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), 10_000_000u32.into())?; CurrentEra::::put(EraIndex::max_value()); - let _ = Ledger::::get(&controller.account()).expect("ledger not created before"); + let _ = Ledger::::get(&controller).expect("ledger not created before"); whitelist_account!(controller); - }: withdraw_unbonded(controller.origin(), s) + }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) verify { - assert!(!Ledger::::contains_key(controller.account())); - assert!(!T::VoterList::contains(&stash.account())); + assert!(!Ledger::::contains_key(controller)); + assert!(!T::VoterList::contains(&stash)); } validate { @@ -295,18 +291,18 @@ benchmarks! { Default::default(), )?; // because it is chilled. - assert!(!T::VoterList::contains(&stash.account())); + assert!(!T::VoterList::contains(&stash)); // Polymesh change // ----------------------------------------------------------------- - add_permissioned_validator_::(stash.did(), Some(2)); + add_permissioned_validator_::(&stash); // ----------------------------------------------------------------- whitelist_account!(controller); - }: _(controller.origin(), ValidatorPrefs::default()) + }: _(RawOrigin::Signed(controller), ValidatorPrefs::default()) verify { - assert!(Validators::::contains_key(stash.account())); - assert!(T::VoterList::contains(&stash.account())); + assert!(Validators::::contains_key(stash.clone())); + assert!(T::VoterList::contains(&stash)); } kick { @@ -326,12 +322,12 @@ benchmarks! { 100, Default::default(), )?; - let stash_lookup = stash.lookup(); + let stash_lookup = T::Lookup::unlookup(stash.clone()); - add_permissioned_validator_::(stash.did(), Some(2)); + add_permissioned_validator_::(&stash); // they start validating. - Staking::::validate(controller.origin().into(), Default::default())?; + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), Default::default())?; // we now create the nominators. there will be `k` of them; each will nominate all // validators. we will then kick each of the `k` nominators from the main validator. @@ -350,14 +346,14 @@ benchmarks! { // optimisations/pessimisations. nominations.insert(i as usize % (nominations.len() + 1), stash_lookup.clone()); // then we nominate. - Staking::::nominate(n_controller.origin().into(), nominations)?; + Staking::::nominate(RawOrigin::Signed(n_controller).into(), nominations)?; - nominator_stashes.push(n_stash.account()); + nominator_stashes.push(n_stash); } // all nominators now should be nominating our validator... for n in nominator_stashes.iter() { - assert!(Nominators::::get(n).unwrap().targets.contains(&stash.account())); + assert!(Nominators::::get(n).unwrap().targets.contains(&stash)); } // we need the unlookuped version of the nominator stash for the kick. @@ -366,11 +362,11 @@ benchmarks! { .collect::>(); whitelist_account!(controller); - }: _(controller.origin(), kicks) + }: _(RawOrigin::Signed(controller), kicks) verify { // all nominators now should *not* be nominating our validator... for n in nominator_stashes.iter() { - assert!(!Nominators::::get(n).unwrap().targets.contains(&stash.account())); + assert!(!Nominators::::get(n).unwrap().targets.contains(&stash)); } } @@ -386,16 +382,16 @@ benchmarks! { 10_000_000, Default::default(), )?; - assert!(!Nominators::::contains_key(&stash.account())); - assert!(!T::VoterList::contains(&stash.account())); + assert!(!Nominators::::contains_key(&stash)); + assert!(!T::VoterList::contains(&stash)); let validators = create_validators::(n, 100).unwrap(); whitelist_account!(controller); - }: _(controller.origin(), validators) + }: _(RawOrigin::Signed(controller), validators) verify { - assert!(Nominators::::contains_key(&stash.account())); - assert!(T::VoterList::contains(&stash.account())) + assert!(Nominators::::contains_key(&stash)); + assert!(T::VoterList::contains(&stash)) } chill { @@ -405,35 +401,36 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; - Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; - assert!(T::VoterList::contains(&stash.account())); - assert!(Validators::::contains_key(&stash.account())); + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; + assert!(T::VoterList::contains(&stash)); + assert!(Validators::::contains_key(&stash)); // ----------------------------------------------------------------- whitelist_account!(controller); - }: _(controller.origin()) + }: _(RawOrigin::Signed(controller)) verify { - assert!(!T::VoterList::contains(&stash.account())); - assert!(!Validators::::contains_key(&stash.account())); + assert!(!T::VoterList::contains(&stash)); + assert!(!Validators::::contains_key(&stash)); } set_payee { let (stash, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; - assert_eq!(Payee::::get(&stash.account()), RewardDestination::Staked); + assert_eq!(Payee::::get(&stash), RewardDestination::Staked); whitelist_account!(controller); - }: _(controller.origin(), RewardDestination::Controller) + }: _(RawOrigin::Signed(controller), RewardDestination::Controller) verify { - assert_eq!(Payee::::get(&stash.account()), RewardDestination::Controller); + assert_eq!(Payee::::get(&stash), RewardDestination::Controller); } set_controller { let (stash, _) = create_stash_controller::(USER_SEED, 100, Default::default())?; let new_controller = create_funded_user::("new_controller", USER_SEED, 100); + let new_controller_lookup = T::Lookup::unlookup(new_controller.clone()); whitelist_account!(stash); - }: _(stash.origin(), new_controller.lookup()) + }: _(RawOrigin::Signed(stash), new_controller_lookup) verify { - assert!(Ledger::::contains_key(&new_controller.account())); + assert!(Ledger::::contains_key(&new_controller)); } set_validator_count { @@ -473,18 +470,18 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(USER_SEED, 10_000_000, Default::default())?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; - Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; - add_slashing_spans::(&stash.account(), s); - assert!(T::VoterList::contains(&stash.account())); - assert!(Validators::::contains_key(&stash.account())); + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; + add_slashing_spans::(&stash, s); + assert!(T::VoterList::contains(&stash)); + assert!(Validators::::contains_key(&stash)); // ----------------------------------------------------------------- - }: _(RawOrigin::Root, stash.account(), s) + }: _(RawOrigin::Root, stash.clone(), s) verify { - assert!(!Ledger::::contains_key(&controller.account())); - assert!(!T::VoterList::contains(&stash.account())); - assert!(!Validators::::contains_key(&stash.account())); + assert!(!Ledger::::contains_key(&controller)); + assert!(!T::VoterList::contains(&stash)); + assert!(!Validators::::contains_key(&stash)); } cancel_deferred_slash { @@ -515,19 +512,19 @@ benchmarks! { let current_era = CurrentEra::::get().unwrap(); // set the commission for this particular era as well. - >::insert(current_era, validator.account().clone(), >::validators(&validator.account())); + >::insert(current_era, validator.clone(), >::validators(&validator)); - let validator_controller = >::get(&validator.account()).unwrap(); + let validator_controller = >::get(&validator).unwrap(); let balance_before = T::Currency::free_balance(&validator_controller); for (_, controller) in &nominators { - let balance = T::Currency::free_balance(&controller.account()); + let balance = T::Currency::free_balance(&controller); ensure!(balance.is_zero(), "Controller has balance, but should be dead."); } - let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller"); - let caller_key = frame_system::Account::::hashed_key_for(&caller.account()); + let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller").account(); + let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: payout_stakers(caller.origin(), validator.account(), current_era) + }: payout_stakers(RawOrigin::Signed(caller), validator, current_era) verify { let balance_after = T::Currency::free_balance(&validator_controller); ensure!( @@ -535,7 +532,7 @@ benchmarks! { "Balance of validator controller should have increased after payout.", ); for (_, controller) in &nominators { - let balance = T::Currency::free_balance(&controller.account()); + let balance = T::Currency::free_balance(&controller); ensure!(!balance.is_zero(), "Payout not given to controller."); } } @@ -555,29 +552,29 @@ benchmarks! { // set the commission for this particular era as well. >::insert( current_era, - validator.account().clone(), - >::validators(&validator.account()) + validator.clone(), + >::validators(&validator) ); - let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller"); - let caller_key = frame_system::Account::::hashed_key_for(&caller.account()); + let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller").account(); + let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - let balance_before = T::Currency::free_balance(&validator.account()); + let balance_before = T::Currency::free_balance(&validator); let mut nominator_balances_before = Vec::new(); for (stash, _) in &nominators { - let balance = T::Currency::free_balance(&stash.account()); + let balance = T::Currency::free_balance(&stash); nominator_balances_before.push(balance); } - }: payout_stakers(caller.origin(), validator.account().clone(), current_era) + }: payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era) verify { - let balance_after = T::Currency::free_balance(&validator.account()); + let balance_after = T::Currency::free_balance(&validator); ensure!( balance_before < balance_after, "Balance of validator stash should have increased after payout.", ); for ((stash, _), balance_before) in nominators.iter().zip(nominator_balances_before.iter()) { - let balance_after = T::Currency::free_balance(&stash.account()); + let balance_after = T::Currency::free_balance(&stash); ensure!( balance_before < &balance_after, "Balance of nominator stash should have increased after payout.", @@ -594,7 +591,7 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 10_000_000, RewardDestination::Staked)?; - let mut staking_ledger = Ledger::::get(controller.account()).unwrap(); + let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { value: 1u32.into(), era: EraIndex::zero(), @@ -602,13 +599,13 @@ benchmarks! { for _ in 0 .. l { staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap() } - Ledger::::insert(controller.account(), staking_ledger.clone()); + Ledger::::insert(controller.clone(), staking_ledger.clone()); let original_bonded: BalanceOf = staking_ledger.active; whitelist_account!(controller); - }: _(controller.origin(), 101u32.into()) + }: _(RawOrigin::Signed(controller.clone()), 101u32.into()) verify { - let ledger = Ledger::::get(&controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; let new_bonded: BalanceOf = ledger.active; assert!(original_bonded < new_bonded); } @@ -620,28 +617,28 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; - Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; - add_slashing_spans::(&stash.account(), s); + add_slashing_spans::(&stash, s); let l = StakingLedger { - stash: stash.account().clone(), + stash: stash.clone(), active: T::Currency::minimum_balance(), total: T::Currency::minimum_balance(), unlocking: Default::default(), claimed_rewards: Default::default(), }; - T::Currency::make_free_balance_be(&stash.account(), 0u32.into()); - Ledger::::insert(&controller.account(), l); + T::Currency::make_free_balance_be(&stash, 0u32.into()); + Ledger::::insert(&controller, l); - assert!(Bonded::::contains_key(&stash.account())); - assert!(T::VoterList::contains(&stash.account())); + assert!(Bonded::::contains_key(&stash)); + assert!(T::VoterList::contains(&stash)); whitelist_account!(controller); - }: _(controller.origin(), stash.account(), s) + }: _(RawOrigin::Signed(controller), stash.clone(), s) verify { - assert!(!Bonded::::contains_key(&stash.account())); - assert!(!T::VoterList::contains(&stash.account())); + assert!(!Bonded::::contains_key(&stash)); + assert!(!T::VoterList::contains(&stash)); } new_era { @@ -717,7 +714,7 @@ benchmarks! { do_slash { let l in 1 .. T::MaxUnlockingChunks::get() as u32; let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; - let mut staking_ledger = Ledger::::get(controller.account()).unwrap(); + let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { value: 1u32.into(), era: EraIndex::zero(), @@ -725,18 +722,18 @@ benchmarks! { for _ in 0 .. l { staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap(); } - Ledger::::insert(controller.account(), staking_ledger); - let balance_before = T::Currency::free_balance(&stash.account()); + Ledger::::insert(controller, staking_ledger); + let balance_before = T::Currency::free_balance(&stash); }: { crate::slashing::do_slash::( - &stash.account(), + &stash, 10u32.into(), &mut BalanceOf::::zero(), &mut NegativeImbalanceOf::::zero(), EraIndex::zero() ); } verify { - let balance_after = T::Currency::free_balance(&stash.account()); + let balance_after = T::Currency::free_balance(&stash); assert!(balance_before > balance_after); } @@ -822,9 +819,9 @@ benchmarks! { let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; - Staking::::validate(controller.origin().into(), validator_prefs)?; - assert!(T::VoterList::contains(&stash.account())); + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), validator_prefs)?; + assert!(T::VoterList::contains(&stash)); Staking::::set_staking_configs( RawOrigin::Root.into(), @@ -836,10 +833,10 @@ benchmarks! { ConfigOp::Set(Zero::zero()), )?; - let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller"); - }: _(caller.origin(), controller.account()) + let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller").account(); + }: _(RawOrigin::Signed(caller), controller) verify { - assert!(!T::VoterList::contains(&stash.account())); + assert!(!T::VoterList::contains(&stash)); } force_apply_min_commission { @@ -852,22 +849,23 @@ benchmarks! { let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; - Staking::::validate(controller.origin().into(), validator_prefs)?; + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller).into(), validator_prefs)?; // Sanity check assert_eq!( - Validators::::get(&stash.account()), + Validators::::get(&stash), ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() } ); // Set the min commission to 75% MinCommission::::set(Perbill::from_percent(75)); - }: _(stash.origin(), stash.account().clone()) + let caller = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), stash.clone()) verify { // The validators commission has been bumped to 75% assert_eq!( - Validators::::get(&stash.account()), + Validators::::get(&stash), ValidatorPrefs { commission: Perbill::from_percent(75), ..Default::default() } ); } @@ -886,9 +884,10 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; Staking::::set_validator_count(RawOrigin::Root.into(), 10).unwrap(); - }: _(RawOrigin::Root, stash.did(), Some(1)) + let did = get_did::(&stash); + }: _(RawOrigin::Root, did, Some(1)) verify { - let identity_preferences = Staking::::permissioned_identity(stash.did()); + let identity_preferences = Staking::::permissioned_identity(did); assert!(identity_preferences.is_some()); assert_eq!(identity_preferences.unwrap().intended_count, 1); } @@ -897,10 +896,11 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - add_permissioned_validator_::(stash.did(), Some(1)); - }: _(RawOrigin::Root, stash.did()) + add_permissioned_validator_::(&stash); + let did = get_did::(&stash); + }: _(RawOrigin::Root, did) verify { - let identity_preferences = Staking::::permissioned_identity(stash.did()); + let identity_preferences = Staking::::permissioned_identity(did); assert!(identity_preferences.is_none()); } @@ -913,10 +913,11 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - add_permissioned_validator_::(stash.did(), Some(1)); - }: _(RawOrigin::Root, stash.did(), 2) + add_permissioned_validator_::(&stash); + let did = get_did::(&stash); + }: _(RawOrigin::Root, did, 2) verify { - assert_eq!(Staking::::permissioned_identity(stash.did()).unwrap().intended_count, 2); + assert_eq!(Staking::::permissioned_identity(did).unwrap().intended_count, 2); } chill_from_governance { @@ -927,9 +928,10 @@ benchmarks! { Staking::::set_validator_count(RawOrigin::Root.into(), 1_000).unwrap(); assert_eq!(Staking::::validator_count(), 1_000); + let did = get_did::(&validator); Staking::::add_permissioned_validator( RawOrigin::Root.into(), - validator.did(), + did, Some(100) ).unwrap(); @@ -937,28 +939,31 @@ benchmarks! { let mut signatories = Vec::new(); for x in 0 .. s { - let key = UserBuilder::::default().seed(x).balance(10_000u32).build("key"); + let key = UserBuilder::::default().seed(x).balance(10_000u32).build("key").account(); + let key_lookup = T::Lookup::unlookup(key.clone()); let _ = T::Currency::issue(10_000u32.into()); + let did = get_did::(&validator); pallet_identity::Pallet::::unsafe_join_identity( - validator.did(), + did, Permissions::default(), - key.account.clone() + key.clone() ); Staking::::bond( - key.origin().into(), - key.lookup(), + RawOrigin::Signed(key.clone()).into(), + key_lookup, 2_000_000u32.into(), RewardDestination::Staked ) .unwrap(); whitelist_account!(key); - Staking::::validate(key.origin().into(), ValidatorPrefs::default()).unwrap(); - assert_eq!(>::contains_key(&key.account), true); - signatories.push(key.account.clone()); + Staking::::validate(RawOrigin::Signed(key.clone()).into(), ValidatorPrefs::default()).unwrap(); + assert_eq!(>::contains_key(&key), true); + signatories.push(key.clone()); } - }: _(RawOrigin::Root, validator.did(), signatories.clone()) + let did = get_did::(&validator); + }: _(RawOrigin::Root, did, signatories.clone()) verify { for key in signatories { assert!(!>::contains_key(&key)); @@ -971,9 +976,9 @@ benchmarks! { let mut stashes = Vec::with_capacity(m as usize); for i in 0 .. m { let stash = create_funded_user::("stash", i, 1000); - stashes.push(stash.account()); + stashes.push(stash.clone()); Validators::::insert( - stash.account(), + stash, ValidatorPrefs { commission: Perbill::from_percent(70), ..Default::default() @@ -1004,14 +1009,15 @@ benchmarks! { )?; for nominator in &nominators { + let did = get_did::(&nominator.0); let claim_first = pallet_identity::Claim1stKey { - target: nominator.0.did(), + target: did, claim_type: ClaimType::CustomerDueDiligence }; let _ = pallet_identity::Claims::::clear_prefix(claim_first, 1, None); } - let nominators: Vec = nominators.iter().map(|x| x.0.account()).collect(); + let nominators: Vec = nominators.iter().map(|x| x.0.clone()).collect(); for nominator in &nominators { assert!(Nominators::::contains_key(nominator)); } diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 99ffdcdeb4..9c4dac9b20 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -289,9 +289,12 @@ pub mod testing_utils; pub mod inflation; pub mod slashing; -pub mod types; pub mod weights; +pub mod permissioned; +pub use permissioned::PolymeshConvertCurve; +pub mod types; + pub mod pallet; use codec::{Decode, Encode, HasCompact, MaxEncodedLen}; @@ -886,8 +889,6 @@ pub trait EraPayout { total_staked: Balance, total_issuance: Balance, era_duration_millis: u64, - max_inflated_issuance: Balance, - non_inflated_yearly_reward: Balance, ) -> (Balance, Balance); } @@ -896,8 +897,6 @@ impl EraPayout for () { _total_staked: Balance, _total_issuance: Balance, _era_duration_millis: u64, - _max_inflated_issuance: Balance, - _non_inflated_yearly_reward: Balance, ) -> (Balance, Balance) { (Default::default(), Default::default()) } @@ -905,26 +904,27 @@ impl EraPayout for () { /// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for /// backwards compatibility. -pub struct ConvertCurve(sp_std::marker::PhantomData); +pub struct ConvertCurve(sp_std::marker::PhantomData<(T, C)>); -impl>> - EraPayout for ConvertCurve +impl< + Balance: AtLeast32BitUnsigned + Clone, + T: Config, + C: Get<&'static PiecewiseLinear<'static>>, + > EraPayout for ConvertCurve { fn era_payout( total_staked: Balance, total_issuance: Balance, era_duration_millis: u64, - max_inflated_issuance: Balance, - non_inflated_yearly_reward: Balance, ) -> (Balance, Balance) { let (validator_payout, max_payout) = inflation::compute_total_payout( - T::get(), + C::get(), total_staked, total_issuance, // Duration of era; more than u64::MAX is rewarded as u64::MAX. era_duration_millis, - max_inflated_issuance, - non_inflated_yearly_reward, + T::MaxVariableInflationTotalIssuance::get(), + T::FixedYearlyReward::get(), ); let rest = max_payout.saturating_sub(validator_payout.clone()); (validator_payout, rest) diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index cdcea46854..d4e8c2de30 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -53,15 +53,11 @@ use super::{pallet::*, STAKING_ID}; // Polymesh change // ----------------------------------------------------------------- -use frame_support::traits::schedule::Anon; -use frame_support::traits::schedule::{DispatchTime, HIGHEST_PRIORITY}; use frame_support::traits::DefensiveSaturating; use polymesh_primitives::traits::IdentityFnTrait; -use polymesh_primitives::IdentityId; -use polymesh_primitives::GC_DID; -use crate::pallet::SlashingSwitch; +use crate::types::PermissionedStaking; use crate::UnlockChunk; // ----------------------------------------------------------------- @@ -120,23 +116,25 @@ impl Pallet { ledger = ledger.consolidate_unlocked(current_era) } - 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 - // portion to fall below existential deposit + will have no more unlocking chunks - // left. We can now safely remove all staking-related information. - Self::kill_stash(&stash, num_slashing_spans)?; - // Remove the lock. - T::Currency::remove_lock(STAKING_ID, &stash); - - ::WeightInfo::withdraw_unbonded_kill(num_slashing_spans) - } else { - // This was the consequence of a partial unbond. just update the ledger and move on. - Self::update_ledger(&controller, &ledger); + // Polymesh change: + // use T::Permissioned::reapable(amount) + let used_weight = if ledger.unlocking.is_empty() && T::Permissioned::reapable(ledger.active) + { + // This account must have called `unbond()` with some value that caused the active + // portion to fall below existential deposit + will have no more unlocking chunks + // left. We can now safely remove all staking-related information. + Self::kill_stash(&stash, num_slashing_spans)?; + // Remove the lock. + T::Currency::remove_lock(STAKING_ID, &stash); + + ::WeightInfo::withdraw_unbonded_kill(num_slashing_spans) + } else { + // This was the consequence of a partial unbond. just update the ledger and move on. + Self::update_ledger(&controller, &ledger); - // This is only an update, so we use less overall weight. - ::WeightInfo::withdraw_unbonded_update(num_slashing_spans) - }; + // This is only an update, so we use less overall weight. + ::WeightInfo::withdraw_unbonded_update(num_slashing_spans) + }; // `old_total` should never be less than the new total because // `consolidate_unlocked` strictly subtracts balance. @@ -152,7 +150,7 @@ impl Pallet { Ok(used_weight) } - pub(super) fn do_payout_stakers( + pub fn do_payout_stakers( validator_stash: T::AccountId, era: EraIndex, ) -> DispatchResultWithPostInfo { @@ -309,7 +307,7 @@ impl Pallet { /// Update the ledger for a controller. /// /// This will also update the stash lock. - pub(crate) fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger) { + pub fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger) { T::Currency::set_lock( STAKING_ID, &ledger.stash, @@ -320,10 +318,10 @@ impl Pallet { } /// Chill a stash account. - pub(crate) fn chill_stash(stash: &T::AccountId) { + pub fn chill_stash(stash: &T::AccountId) { // Polymesh change // ----------------------------------------------------------------- - Self::release_running_validator(stash); + T::Permissioned::on_chill(stash); // ----------------------------------------------------------------- let chilled_as_validator = Self::do_remove_validator(stash); let chilled_as_nominator = Self::do_remove_nominator(stash); @@ -496,51 +494,13 @@ impl Pallet { let era_duration = (now_as_millis_u64 - active_era_start).saturated_into::(); let staked = Self::eras_total_stake(&active_era.index); let issuance = T::Currency::total_issuance(); - let (validator_payout, remainder) = T::EraPayout::era_payout( - staked, - issuance, - era_duration, - T::MaxVariableInflationTotalIssuance::get(), - T::FixedYearlyReward::get(), - ); + let (validator_payout, remainder) = + T::EraPayout::era_payout(staked, issuance, era_duration); // Polymesh change // ----------------------------------------------------------------- // Schedule rewards - let next_block_number = >::block_number() + 1u32.into(); - for (index, validator_id) in T::SessionInterface::validators().into_iter().enumerate() { - let schedule_block_number = - next_block_number + index.saturated_into::(); - match T::RewardScheduler::schedule( - DispatchTime::At(schedule_block_number), - None, - HIGHEST_PRIORITY, - RawOrigin::Root.into(), - Call::::payout_stakers_by_system { - validator_stash: validator_id.clone(), - era: active_era.index, - }.into() - ) { - Ok(_) => log!( - info, - "💸 Rewards are successfully scheduled for validator id: {:?} at block number: {:?}", - &validator_id, - schedule_block_number, - ), - Err(error) => { - log!( - error, - "⛔ Detected error in scheduling the reward payment: {:?}", - error - ); - Self::deposit_event(Event::::RewardPaymentSchedulingInterrupted { - account_id: validator_id, - era: active_era.index, - error - }); - } - } - } + T::Permissioned::schedule_payouts(&active_era); // ----------------------------------------------------------------- Self::deposit_event(Event::::EraPaid { @@ -890,7 +850,8 @@ impl Pallet { if let Some(Nominations { targets, .. }) = >::get(&voter) { nominators_seen.saturating_inc(); - if Self::is_nominator_compliant(&voter) { + // Polymesh change: check if the nominator is compliant + if T::Permissioned::is_nominator_compliant(&voter) { let voter_weight = weight_of(&voter); if !targets.is_empty() { all_voters.push((voter.clone(), voter_weight, targets)); @@ -903,9 +864,8 @@ impl Pallet { } } else if Validators::::contains_key(&voter) { validators_seen.saturating_inc(); - if Self::is_validator_compliant(&voter) - && Self::is_validator_active_balance_valid(&voter) - { + // Polymesh change: check if the validator is compliant + if T::Permissioned::is_validator_compliant(&voter) { // if this voter is a validator: let self_vote = ( voter.clone(), @@ -980,9 +940,8 @@ impl Pallet { if Validators::::contains_key(&target) { validators_seen.saturating_inc(); - if Self::is_validator_compliant(&target) - && Self::is_validator_active_balance_valid(&target) - { + // Polymesh change: check if the validator is compliant + if T::Permissioned::is_validator_compliant(&target) { all_targets.push(target); } } @@ -1096,62 +1055,6 @@ impl Pallet { ); } - // Polymesh change - // ----------------------------------------------------------------- - - /// Returns `true` if active balance is above [`MinValidatorBond`]. Otherwise, returns `false`. - pub(crate) fn is_validator_active_balance_valid(who: &T::AccountId) -> bool { - if let Some(controller) = Self::bonded(&who) { - if let Some(ledger) = Self::ledger(&controller) { - return ledger.active >= MinValidatorBond::::get(); - } - } - false - } - - /// Returns `true` if `stash` has a valid cdd claim and is permissioned. Otherwise, returns `false`. - pub(crate) fn is_validator_compliant(stash: &T::AccountId) -> bool { - pallet_identity::Pallet::::get_identity(stash).map_or(false, |id| { - pallet_identity::Pallet::::has_valid_cdd(id) - && Self::permissioned_identity(id).is_some() - }) - } - - /// Returns `true` if `who` has a valid cdd claim. Otherwise, returns `false`. - pub(crate) fn is_nominator_compliant(who: &T::AccountId) -> bool { - pallet_identity::Pallet::::get_identity(who) - .map_or(false, |id| pallet_identity::Pallet::::has_valid_cdd(id)) - } - - pub(crate) fn get_bonding_duration_period() -> u64 { - (T::SessionsPerEra::get() * T::BondingDuration::get()) as u64 // total session - * T::EpochDuration::get() // session length - * T::ExpectedBlockTime::get().saturated_into::() - } - - /// Decrease the running count of validators by 1 for the stash identity. - pub(crate) fn release_running_validator(stash: &T::AccountId) { - if !>::contains_key(stash) { - return; - } - - if let Some(did) = pallet_identity::Pallet::::get_identity(stash) { - PermissionedIdentity::::mutate(&did, |preferences| { - if let Some(p) = preferences { - if p.running_count > 0 { - p.running_count -= 1; - } - } - }); - pallet_identity::Pallet::::remove_account_key_ref_count(&stash); - } - } - - /// Returns the maximum number of validators per identiy - pub fn maximum_number_of_validators_per_identity() -> u32 { - (T::MaxValidatorPerIdentity::get() * Self::validator_count()).max(1) - } - pub fn unbond_balance( controller_account: T::AccountId, ledger: &mut StakingLedger, @@ -1219,40 +1122,6 @@ impl Pallet { Ok(()) } - - pub(crate) fn base_chill_from_governance( - origin: T::RuntimeOrigin, - identity: IdentityId, - stash_keys: Vec, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - for key in &stash_keys { - let key_did = pallet_identity::Pallet::::get_identity(&key); - // Checks if the stash key identity is the same as the identity given. - ensure!(key_did == Some(identity), Error::::NotStash); - // Checks if the key is a validator if not returns an error. - ensure!( - >::contains_key(&key), - Error::::ValidatorNotFound - ); - } - - for key in stash_keys { - Self::chill_stash(&key); - } - - // Change identity status to be Non-Permissioned - PermissionedIdentity::::remove(&identity); - - Self::deposit_event(Event::::PermissionedIdentityRemoved { - governance_councill_did: GC_DID, - validators_identity: identity, - }); - Ok(()) - } - - // ----------------------------------------------------------------- } impl Pallet { @@ -1569,10 +1438,10 @@ where // Polymesh change // ----------------------------------------------------------------- let slash_fraction_none = vec![Perbill::from_parts(0); slash_fraction.len()]; - let slash_fraction = if SlashingAllowedFor::::get() == SlashingSwitch::None { - slash_fraction_none.as_slice() - } else { + let slash_fraction = if T::Permissioned::is_slashing_enabled() { slash_fraction + } else { + slash_fraction_none.as_slice() }; // ----------------------------------------------------------------- diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index c039bd6550..5c70afad56 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -53,15 +53,14 @@ use crate::{ use frame_support::traits::schedule::Anon; use frame_support::traits::IsSubType; use frame_system::offchain::SendTransactionTypes; -use sp_runtime::traits::{AccountIdConversion, Dispatchable}; +use sp_runtime::traits::Dispatchable; use sp_runtime::Permill; use pallet_identity::Config as IdentityConfig; -use polymesh_primitives::constants::GC_PALLET_ID; use polymesh_primitives::GC_DID; use polymesh_primitives::{storage_migration_ver, traits::IdentityFnTrait, IdentityId}; -use crate::types::{PermissionedIdentityPrefs, SlashingSwitch}; +use crate::types::{PermissionedIdentityPrefs, PermissionedStaking, SlashingSwitch}; // ----------------------------------------------------------------- const STAKING_ID: LockIdentifier = *b"staking "; @@ -328,6 +327,9 @@ pub mod pallet { // Polymesh change // ----------------------------------------------------------------- + /// Permissioned staking. + type Permissioned: PermissionedStaking; + /// Maximum amount of validators that can run by an identity. /// It will be MaxValidatorPerIdentity * Self::validator_count(). #[pallet::constant] @@ -1097,6 +1099,7 @@ pub mod pallet { return Err(Error::::AlreadyBonded.into()); } + // Polymesh added: // An existing controller cannot become a stash. if >::contains_key(&stash) { return Err(Error::::AlreadyPaired.into()); @@ -1327,19 +1330,6 @@ pub mod pallet { Error::::CommissionTooLow ); - // ensure their commission is correct. - ensure!( - prefs.commission <= Self::validator_commission_cap(), - Error::::CommissionTooHigh - ); - - // Polymesh change - // ----------------------------------------------------------------- - let stash_did = pallet_identity::Pallet::::get_identity(stash) - .ok_or(Error::::StashIdentityDoesNotExist)?; - let mut stash_did_prefs = Self::permissioned_identity(stash_did) - .ok_or(Error::::StashIdentityNotPermissioned)?; - // Only check limits if they are not already a validator. if !Validators::::contains_key(stash) { // If this error is reached, we need to adjust the `MinValidatorBond` and start @@ -1351,16 +1341,11 @@ pub mod pallet { Error::::TooManyValidators ); } - // Ensure the identity doesn't run more validators than the intended count - ensure!( - stash_did_prefs.running_count < stash_did_prefs.intended_count, - Error::::TooManyValidators - ); - stash_did_prefs.running_count += 1; - pallet_identity::Pallet::::add_account_key_ref_count(&stash); - PermissionedIdentity::::insert(stash_did, stash_did_prefs); + // Polymesh change + // ----------------------------------------------------------------- + T::Permissioned::on_validate(stash, prefs.commission)?; + // ----------------------------------------------------------------- } - // ----------------------------------------------------------------- Self::do_remove_nominator(stash); Self::do_add_validator(stash, prefs.clone()); @@ -1436,18 +1421,10 @@ pub mod pallet { // Polymesh change // ----------------------------------------------------------------- + T::Permissioned::on_nominate(&stash)?; + let nominator_did = pallet_identity::Pallet::::get_identity(&stash) .ok_or(Error::::StashIdentityDoesNotExist)?; - let bounding_duration_period = Self::get_bonding_duration_period() as u32; - - if let None = pallet_identity::Pallet::::fetch_cdd( - nominator_did, - bounding_duration_period.into(), - ) { - return Err(Error::::StashIdentityNotCDDed.into()); - } - - Self::release_running_validator(&stash); Self::deposit_event(Event::::Nominated { nominator_identity: nominator_did, stash: ledger.stash.clone(), @@ -1535,6 +1512,7 @@ pub mod pallet { return Err(Error::::AlreadyPaired.into()); } + // Polymesh added: // Prevents stashes which are controllers of another ledger from calling the extrinsic if old_controller != stash { if >::contains_key(&stash) { @@ -1845,12 +1823,14 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let _ = ensure_signed(origin)?; - let ed = T::Currency::minimum_balance(); - let reapable = T::Currency::total_balance(&stash) <= ed - || Self::ledger(Self::bonded(stash.clone()).ok_or(Error::::NotStash)?) - .map(|l| l.total) - .unwrap_or_default() - < ed; + // Polymesh change: + // use T::Permissioned::reapable(amount) + let reapable = T::Permissioned::reapable(T::Currency::total_balance(&stash)) + || T::Permissioned::reapable( + Self::ledger(Self::bonded(stash.clone()).ok_or(Error::::NotStash)?) + .map(|l| l.total) + .unwrap_or_default(), + ); ensure!(reapable, Error::::FundedTarget); Self::kill_stash(&stash, num_slashing_spans)?; @@ -2089,42 +2069,7 @@ pub mod pallet { identity: IdentityId, intended_count: Option, ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!( - Self::permissioned_identity(&identity).is_none(), - Error::::IdentityIsAlreadyPermissioned - ); - - ensure!( - pallet_identity::Pallet::::has_valid_cdd(identity), - Error::::IdentityIsMissingCDD - ); - - match intended_count { - Some(intended_count) => { - ensure!( - intended_count < Self::maximum_number_of_validators_per_identity(), - Error::::IntendedCountIsExceedingConsensusLimit - ); - PermissionedIdentity::::insert( - &identity, - PermissionedIdentityPrefs::new(intended_count), - ); - } - None => { - PermissionedIdentity::::insert( - &identity, - PermissionedIdentityPrefs::default(), - ); - } - } - - Self::deposit_event(Event::::PermissionedIdentityAdded { - governance_councill_did: GC_DID, - validators_identity: identity, - }); - Ok(()) + Self::base_add_permissioned_validator(origin, identity, intended_count) } /// Remove an identity from the pool of (wannabe) validator identities. Effects are known in the next session. @@ -2140,20 +2085,7 @@ pub mod pallet { origin: OriginFor, identity: IdentityId, ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!( - Self::permissioned_identity(&identity).is_some(), - Error::::IdentityNotFound - ); - - PermissionedIdentity::::remove(&identity); - - Self::deposit_event(Event::::PermissionedIdentityRemoved { - governance_councill_did: GC_DID, - validators_identity: identity, - }); - Ok(()) + Self::base_remove_permissioned_validator(origin, identity) } /// Validate the nominators CDD expiry time. @@ -2167,49 +2099,7 @@ pub mod pallet { origin: OriginFor, targets: Vec, ) -> DispatchResult { - ensure_root(origin.clone())?; - - ensure!(!targets.is_empty(), Error::::EmptyTargets); - - let mut expired_nominators = Vec::new(); - // Iterate provided list of accountIds (These accountIds should be stash type account). - for target in targets - .iter() - // Nominator must be vouching for someone. - .filter(|target| Self::nominators(target).is_some()) - // Access the DIDs of the nominators whose CDDs have expired. - .filter(|target| { - // Fetch all the claim values provided by the trusted service providers - // There is a possibility that nominator will have more than one claim for the same key, - // So we iterate all of them and if any one of the claim value doesn't expire then nominator posses - // valid CDD otherwise it will be removed from the pool of the nominators. - // If the target has no DID, it's also removed. - pallet_identity::Pallet::::get_identity(&target) - .filter(|did| pallet_identity::Pallet::::has_valid_cdd(*did)) - .is_none() - }) - { - // Un-bonding the balance that bonded with the controller account of a Stash account - // This unbonded amount only be accessible after completion of the BondingDuration - // Controller account need to call the dispatchable function `withdraw_unbond` to withdraw fund. - - let controller = Self::bonded(target).ok_or(Error::::NotStash)?; - let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let active_balance = ledger.active; - if ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize { - Self::unbond_balance(controller, &mut ledger, active_balance)?; - - expired_nominators.push(target.clone()); - // Free the nominator from the valid nominator list - >::remove(target); - } - } - Self::deposit_event(Event::::InvalidatedNominators { - governance_councill_did: GC_DID, - governance_councill_account: GC_PALLET_ID.into_account_truncating(), - expired_nominators: expired_nominators, - }); - Ok(()) + Self::base_validate_cdd_expiry_nominators(origin, targets) } #[pallet::call_index(29)] @@ -2221,8 +2111,7 @@ pub mod pallet { validator_stash: T::AccountId, era: EraIndex, ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - Self::do_payout_stakers(validator_stash, era) + Self::base_payout_stakers_by_system(origin, validator_stash, era) } /// Switch slashing status on the basis of given `slashing_switch`. Can only be called by root. @@ -2232,10 +2121,7 @@ pub mod pallet { origin: OriginFor, slashing_switch: SlashingSwitch, ) -> DispatchResult { - ensure_root(origin)?; - SlashingAllowedFor::::put(slashing_switch); - Self::deposit_event(Event::::SlashingAllowedForChanged { slashing_switch }); - Ok(()) + Self::base_change_slashing_allowed_for(origin, slashing_switch) } /// Sets the intended count to `new_intended_count` for the given `identity`. @@ -2246,18 +2132,11 @@ pub mod pallet { identity: IdentityId, new_intended_count: u32, ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!( - Self::maximum_number_of_validators_per_identity() > new_intended_count, - Error::::IntendedCountIsExceedingConsensusLimit - ); - - PermissionedIdentity::::try_mutate(&identity, |pref| { - pref.as_mut() - .ok_or_else(|| Error::::IdentityNotFound.into()) - .map(|p| p.intended_count = new_intended_count) - }) + Self::base_update_permissioned_validator_intended_count( + origin, + identity, + new_intended_count, + ) } /// Governance council forcefully chills a validator. Effects will be felt at the beginning of the next era. @@ -2279,28 +2158,7 @@ pub mod pallet { #[pallet::call_index(33)] #[pallet::weight(::WeightInfo::set_commission_cap(150))] pub fn set_commission_cap(origin: OriginFor, new_cap: Perbill) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin.clone())?; - - // Update the cap, assuming it changed, or error. - let old_cap = - ValidatorCommissionCap::::try_mutate(|cap| -> Result<_, DispatchError> { - ensure!(*cap != new_cap, Error::::CommissionUnchanged); - Ok(core::mem::replace(cap, new_cap)) - })?; - - // Update `commission` in each validator prefs to `min(comission, new_cap)`. - >::translate(|_, mut prefs: ValidatorPrefs| { - prefs.commission = prefs.commission.min(new_cap); - Some(prefs) - }); - - Self::deposit_event(Event::::CommissionCapUpdated { - governance_councill_did: GC_DID, - old_commission_cap: old_cap, - new_commission_cap: new_cap, - }); - - Ok(()) + Self::base_set_commission_cap(origin, new_cap) } // ----------------------------------------------------------------- } diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs new file mode 100644 index 0000000000..fee9473252 --- /dev/null +++ b/pallets/staking/src/permissioned.rs @@ -0,0 +1,434 @@ +use frame_support::{pallet_prelude::*, traits::Get}; +use frame_system::RawOrigin; +use frame_system::{ensure_root, pallet_prelude::*}; +use sp_runtime::traits::SaturatedConversion; +use sp_std::prelude::*; + +use frame_support::traits::schedule::Anon; +use frame_support::traits::schedule::{DispatchTime, HIGHEST_PRIORITY}; +use frame_support::traits::Currency; + +use polymesh_primitives::constants::GC_PALLET_ID; +use polymesh_primitives::IdentityId; +use polymesh_primitives::GC_DID; +#[cfg(feature = "runtime-benchmarks")] +use polymesh_primitives::{AuthorizationData, Permissions, Signatory}; + +use sp_runtime::traits::AccountIdConversion; + +use crate::types::{PermissionedIdentityPrefs, PermissionedStaking, SlashingSwitch, WhoToSlash}; +use crate::*; + +/// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for +/// backwards compatibility. +pub struct PolymeshConvertCurve(sp_std::marker::PhantomData<(T, C)>); + +impl< + Balance: AtLeast32BitUnsigned + Clone, + T: Config, + C: Get<&'static PiecewiseLinear<'static>>, + > EraPayout for PolymeshConvertCurve +{ + fn era_payout( + total_staked: Balance, + total_issuance: Balance, + era_duration_millis: u64, + ) -> (Balance, Balance) { + let (validator_payout, max_payout) = inflation::compute_total_payout( + C::get(), + total_staked, + total_issuance, + // Duration of era; more than u64::MAX is rewarded as u64::MAX. + era_duration_millis, + T::MaxVariableInflationTotalIssuance::get(), + T::FixedYearlyReward::get(), + ); + let rest = max_payout.saturating_sub(validator_payout.clone()); + (validator_payout, rest) + } +} + +impl PermissionedStaking for Pallet { + /// Permission a validator. + #[cfg(feature = "runtime-benchmarks")] + fn permission_validator(stash: &T::AccountId) { + let did = + pallet_identity::Pallet::::get_identity(stash).expect("Failed to get identity"); + Pallet::::add_permissioned_validator(RawOrigin::Root.into(), did, Some(2)) + .expect("Failed to add permissioned validator"); + } + + /// Setup stash and controller. + #[cfg(feature = "runtime-benchmarks")] + fn setup_stash_and_controller(stash: &T::AccountId, controller: &T::AccountId) { + let did = + pallet_identity::Pallet::::get_identity(stash).expect("Failed to get identity"); + let auth_id = pallet_identity::Pallet::::add_auth( + did, + Signatory::Account(controller.clone()), + AuthorizationData::JoinIdentity(Permissions::default()), + None, + ) + .expect("Failed to add auth"); + pallet_identity::Pallet::::join_identity_as_key( + RawOrigin::Signed(controller.clone()).into(), + auth_id, + ) + .expect("Failed to join identity as key"); + } + + /// Check if amount is under the existential deposit. + fn reapable(amount: BalanceOf) -> bool { + amount <= T::Currency::minimum_balance() + } + + fn on_validate(stash: &T::AccountId, commission: Perbill) -> DispatchResult { + // ensure their commission is correct. + ensure!( + commission <= ValidatorCommissionCap::::get(), + Error::::CommissionTooHigh + ); + + let stash_did = pallet_identity::Pallet::::get_identity(stash) + .ok_or(Error::::StashIdentityDoesNotExist)?; + let mut stash_did_prefs = PermissionedIdentity::::get(stash_did) + .ok_or(Error::::StashIdentityNotPermissioned)?; + + // Ensure the identity doesn't run more validators than the intended count + ensure!( + stash_did_prefs.running_count < stash_did_prefs.intended_count, + Error::::TooManyValidators + ); + stash_did_prefs.running_count += 1; + pallet_identity::Pallet::::add_account_key_ref_count(&stash); + PermissionedIdentity::::insert(stash_did, stash_did_prefs); + + Ok(()) + } + + /// On chill hook. + fn on_chill(stash: &T::AccountId) { + Self::release_running_validator(stash); + } + + /// On nominate hook. + fn on_nominate(stash: &T::AccountId) -> DispatchResult { + let nominator_did = pallet_identity::Pallet::::get_identity(&stash) + .ok_or(Error::::StashIdentityDoesNotExist)?; + let bounding_duration_period = Self::get_bonding_duration_period() as u32; + + if let None = + pallet_identity::Pallet::::fetch_cdd(nominator_did, bounding_duration_period.into()) + { + return Err(Error::::StashIdentityNotCDDed.into()); + } + + Self::release_running_validator(&stash); + Ok(()) + } + + /// Returns `true` if `stash` has a valid cdd claim and is permissioned. Otherwise, returns `false`. + fn is_validator_compliant(stash: &T::AccountId) -> bool { + pallet_identity::Pallet::::get_identity(stash).map_or(false, |id| { + pallet_identity::Pallet::::has_valid_cdd(id) + && PermissionedIdentity::::get(id).is_some() + && Self::is_validator_active_balance_valid(stash) + }) + } + + /// Returns `true` if `who` has a valid cdd claim. Otherwise, returns `false`. + fn is_nominator_compliant(who: &T::AccountId) -> bool { + pallet_identity::Pallet::::get_identity(who) + .map_or(false, |id| pallet_identity::Pallet::::has_valid_cdd(id)) + } + + /// Schedule reward payouts. + fn schedule_payouts(active_era: &ActiveEraInfo) { + let next_block_number = >::block_number() + 1u32.into(); + for (index, validator_id) in T::SessionInterface::validators().into_iter().enumerate() { + let schedule_block_number = + next_block_number + index.saturated_into::(); + match T::RewardScheduler::schedule( + DispatchTime::At(schedule_block_number), + None, + HIGHEST_PRIORITY, + RawOrigin::Root.into(), + Call::::payout_stakers_by_system { + validator_stash: validator_id.clone(), + era: active_era.index, + }.into() + ) { + Ok(_) => log!( + info, + "💸 Rewards are successfully scheduled for validator id: {:?} at block number: {:?}", + &validator_id, + schedule_block_number, + ), + Err(error) => { + log!( + error, + "⛔ Detected error in scheduling the reward payment: {:?}", + error + ); + Pallet::::deposit_event(Event::::RewardPaymentSchedulingInterrupted { + account_id: validator_id, + era: active_era.index, + error + }); + } + } + } + } + + /// Who should be slashed? + /// Returns `None` if no one should be slashed. + fn who_to_slash() -> Option { + SlashingAllowedFor::::get().into() + } +} + +impl Pallet { + /// Returns `true` if active balance is above [`MinValidatorBond`]. Otherwise, returns `false`. + pub(crate) fn is_validator_active_balance_valid(who: &T::AccountId) -> bool { + if let Some(controller) = Bonded::::get(&who) { + if let Some(ledger) = Ledger::::get(&controller) { + return ledger.active >= MinValidatorBond::::get(); + } + } + false + } + + pub(crate) fn get_bonding_duration_period() -> u64 { + (T::SessionsPerEra::get() * T::BondingDuration::get()) as u64 // total session + * T::EpochDuration::get() // session length + * T::ExpectedBlockTime::get().saturated_into::() + } + + /// Decrease the running count of validators by 1 for the stash identity. + pub(crate) fn release_running_validator(stash: &T::AccountId) { + if !>::contains_key(stash) { + return; + } + + if let Some(did) = pallet_identity::Pallet::::get_identity(stash) { + PermissionedIdentity::::mutate(&did, |preferences| { + if let Some(p) = preferences { + if p.running_count > 0 { + p.running_count -= 1; + } + } + }); + pallet_identity::Pallet::::remove_account_key_ref_count(&stash); + } + } + + /// Returns the maximum number of validators per identiy + pub fn maximum_number_of_validators_per_identity() -> u32 { + (T::MaxValidatorPerIdentity::get() * ValidatorCount::::get()).max(1) + } + + pub(crate) fn base_add_permissioned_validator( + origin: OriginFor, + identity: IdentityId, + intended_count: Option, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + ensure!( + Self::permissioned_identity(&identity).is_none(), + Error::::IdentityIsAlreadyPermissioned + ); + + ensure!( + pallet_identity::Pallet::::has_valid_cdd(identity), + Error::::IdentityIsMissingCDD + ); + + match intended_count { + Some(intended_count) => { + ensure!( + intended_count < Self::maximum_number_of_validators_per_identity(), + Error::::IntendedCountIsExceedingConsensusLimit + ); + PermissionedIdentity::::insert( + &identity, + PermissionedIdentityPrefs::new(intended_count), + ); + } + None => { + PermissionedIdentity::::insert(&identity, PermissionedIdentityPrefs::default()); + } + } + + Pallet::::deposit_event(Event::::PermissionedIdentityAdded { + governance_councill_did: GC_DID, + validators_identity: identity, + }); + Ok(()) + } + + pub(crate) fn base_remove_permissioned_validator( + origin: OriginFor, + identity: IdentityId, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + ensure!( + Self::permissioned_identity(&identity).is_some(), + Error::::IdentityNotFound + ); + + PermissionedIdentity::::remove(&identity); + + Pallet::::deposit_event(Event::::PermissionedIdentityRemoved { + governance_councill_did: GC_DID, + validators_identity: identity, + }); + Ok(()) + } + + pub(crate) fn base_validate_cdd_expiry_nominators( + origin: OriginFor, + targets: Vec, + ) -> DispatchResult { + ensure_root(origin.clone())?; + + ensure!(!targets.is_empty(), Error::::EmptyTargets); + + let mut expired_nominators = Vec::new(); + // Iterate provided list of accountIds (These accountIds should be stash type account). + for target in targets + .iter() + // Nominator must be vouching for someone. + .filter(|target| Self::nominators(target).is_some()) + // Access the DIDs of the nominators whose CDDs have expired. + .filter(|target| { + // Fetch all the claim values provided by the trusted service providers + // There is a possibility that nominator will have more than one claim for the same key, + // So we iterate all of them and if any one of the claim value doesn't expire then nominator posses + // valid CDD otherwise it will be removed from the pool of the nominators. + // If the target has no DID, it's also removed. + pallet_identity::Pallet::::get_identity(&target) + .filter(|did| pallet_identity::Pallet::::has_valid_cdd(*did)) + .is_none() + }) + { + // Un-bonding the balance that bonded with the controller account of a Stash account + // This unbonded amount only be accessible after completion of the BondingDuration + // Controller account need to call the dispatchable function `withdraw_unbond` to withdraw fund. + + let controller = Self::bonded(target).ok_or(Error::::NotStash)?; + let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; + let active_balance = ledger.active; + if ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize { + Self::unbond_balance(controller, &mut ledger, active_balance)?; + + expired_nominators.push(target.clone()); + // Free the nominator from the valid nominator list + >::remove(target); + } + } + Pallet::::deposit_event(Event::::InvalidatedNominators { + governance_councill_did: GC_DID, + governance_councill_account: GC_PALLET_ID.into_account_truncating(), + expired_nominators: expired_nominators, + }); + Ok(()) + } + + pub(crate) fn base_payout_stakers_by_system( + origin: OriginFor, + validator_stash: T::AccountId, + era: EraIndex, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + Self::do_payout_stakers(validator_stash, era) + } + + pub(crate) fn base_change_slashing_allowed_for( + origin: OriginFor, + slashing_switch: SlashingSwitch, + ) -> DispatchResult { + ensure_root(origin)?; + SlashingAllowedFor::::put(slashing_switch); + Pallet::::deposit_event(Event::::SlashingAllowedForChanged { slashing_switch }); + Ok(()) + } + + pub(crate) fn base_update_permissioned_validator_intended_count( + origin: OriginFor, + identity: IdentityId, + new_intended_count: u32, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + ensure!( + Self::maximum_number_of_validators_per_identity() > new_intended_count, + Error::::IntendedCountIsExceedingConsensusLimit + ); + + PermissionedIdentity::::try_mutate(&identity, |pref| { + pref.as_mut() + .ok_or_else(|| Error::::IdentityNotFound.into()) + .map(|p| p.intended_count = new_intended_count) + }) + } + + pub(crate) fn base_set_commission_cap( + origin: OriginFor, + new_cap: Perbill, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin.clone())?; + + // Update the cap, assuming it changed, or error. + let old_cap = ValidatorCommissionCap::::try_mutate(|cap| -> Result<_, DispatchError> { + ensure!(*cap != new_cap, Error::::CommissionUnchanged); + Ok(core::mem::replace(cap, new_cap)) + })?; + + // Update `commission` in each validator prefs to `min(comission, new_cap)`. + >::translate(|_, mut prefs: ValidatorPrefs| { + prefs.commission = prefs.commission.min(new_cap); + Some(prefs) + }); + + Pallet::::deposit_event(Event::::CommissionCapUpdated { + governance_councill_did: GC_DID, + old_commission_cap: old_cap, + new_commission_cap: new_cap, + }); + + Ok(()) + } + pub(crate) fn base_chill_from_governance( + origin: T::RuntimeOrigin, + identity: IdentityId, + stash_keys: Vec, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + for key in &stash_keys { + let key_did = pallet_identity::Pallet::::get_identity(&key); + // Checks if the stash key identity is the same as the identity given. + ensure!(key_did == Some(identity), Error::::NotStash); + // Checks if the key is a validator if not returns an error. + ensure!( + >::contains_key(&key), + Error::::ValidatorNotFound + ); + } + + for key in stash_keys { + Self::chill_stash(&key); + } + + // Change identity status to be Non-Permissioned + PermissionedIdentity::::remove(&identity); + + Pallet::::deposit_event(Event::::PermissionedIdentityRemoved { + governance_councill_did: GC_DID, + validators_identity: identity, + }); + Ok(()) + } +} diff --git a/pallets/staking/src/slashing.rs b/pallets/staking/src/slashing.rs index 5314bb97a2..e7ca928177 100644 --- a/pallets/staking/src/slashing.rs +++ b/pallets/staking/src/slashing.rs @@ -50,8 +50,8 @@ //! Based on research at use crate::{ - types::SlashingSwitch, BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, Pallet, - Perbill, SessionInterface, SlashingAllowedFor, Store, UnappliedSlash, + types::PermissionedStaking, BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, Pallet, + Perbill, SessionInterface, Store, UnappliedSlash, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ @@ -303,7 +303,7 @@ pub(crate) fn compute_slash( // Polymesh change // ----------------------------------------------------------------- let mut nominators_slashed = Vec::new(); - if SlashingAllowedFor::::get() == SlashingSwitch::ValidatorAndNominator { + if T::Permissioned::slash_nominators() { reward_payout += slash_nominators::(params.clone(), prior_slash_p, &mut nominators_slashed); } @@ -665,7 +665,7 @@ pub(crate) fn apply_slash( // Polymesh change // ----------------------------------------------------------------- - if SlashingAllowedFor::::get() == SlashingSwitch::ValidatorAndNominator { + if T::Permissioned::slash_nominators() { for &(ref nominator, nominator_slash) in &unapplied_slash.others { do_slash::( nominator, diff --git a/pallets/staking/src/testing_utils.rs b/pallets/staking/src/testing_utils.rs index 089673a73b..d4267fbe9f 100644 --- a/pallets/staking/src/testing_utils.rs +++ b/pallets/staking/src/testing_utils.rs @@ -36,8 +36,8 @@ const SEED: u32 = 0; // Polymesh change // ----------------------------------------------------------------- -use pallet_identity::benchmarking::{User, UserBuilder}; -use polymesh_primitives::{AuthorizationData, Permissions, Signatory}; +use crate::types::PermissionedStaking; +use pallet_identity::benchmarking::UserBuilder; // ----------------------------------------------------------------- /// This function removes all validators and nominators from storage. @@ -54,10 +54,7 @@ pub fn clear_validators_and_nominators() { } /// Grab a funded user. -pub fn create_funded_user(string: &'static str, n: u32, balance: u32) -> User -where - T: Config, -{ +pub fn create_funded_user(string: &'static str, n: u32, balance: u32) -> T::AccountId { // Polymesh change // ----------------------------------------------------------------- let _ = T::Currency::issue(balance.into()); @@ -66,39 +63,32 @@ where .seed(n) .generate_did() .build(string) + .account() // ----------------------------------------------------------------- } /// Create a stash and controller pair. -pub fn create_stash_controller( +pub fn create_stash_controller( n: u32, balance: u32, destination: RewardDestination, -) -> Result<(User, User), &'static str> -where - T: Config, -{ +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance); let controller = UserBuilder::::default() .balance(balance) .seed(n) - .build("controller"); + .build("controller") + .account(); + let controller_lookup = T::Lookup::unlookup(controller.clone()); // Polymesh change // ----------------------------------------------------------------- // Attach the controller key as secondary key of the stash - let auth_id = pallet_identity::Pallet::::add_auth( - stash.did(), - Signatory::Account(controller.account()), - AuthorizationData::JoinIdentity(Permissions::default()), - None, - )?; - pallet_identity::Pallet::::join_identity_as_key(controller.origin().into(), auth_id)?; + T::Permissioned::setup_stash_and_controller(&stash, &controller); // ----------------------------------------------------------------- - let controller_lookup = controller.lookup(); Staking::::bond( - stash.origin().into(), + RawOrigin::Signed(stash.clone()).into(), controller_lookup, (balance / 10).into(), destination, @@ -112,34 +102,19 @@ pub fn create_stash_and_dead_controller( n: u32, balance: u32, destination: RewardDestination, -) -> Result<(User, User), &'static str> -where - T: Config, -{ +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance); - let controller_account: T::AccountId = account("controller", n, 100); - let controller = User { - account: controller_account.clone(), - origin: RawOrigin::Signed(controller_account), - did: None, - secret: None, - }; + let controller: T::AccountId = account("controller", n, 100); + let controller_lookup = T::Lookup::unlookup(controller.clone()); // Polymesh change // ----------------------------------------------------------------- // Attach the controller key as secondary key of the stash - let auth_id = pallet_identity::Pallet::::add_auth( - stash.did(), - Signatory::Account(controller.account()), - AuthorizationData::JoinIdentity(Permissions::default()), - None, - )?; - pallet_identity::Pallet::::join_identity_as_key(controller.origin().into(), auth_id)?; + T::Permissioned::setup_stash_and_controller(&stash, &controller); // ----------------------------------------------------------------- - let controller_lookup = controller.lookup(); Staking::::bond( - stash.origin().into(), + RawOrigin::Signed(stash.clone()).into(), controller_lookup, (balance / 10).into(), destination, @@ -178,10 +153,11 @@ where }; // Polymesh change // ----------------------------------------------------------------- - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash); // ----------------------------------------------------------------- - Staking::::validate(controller.origin().into(), validator_prefs)?; - validators.push(stash.lookup()); + Staking::::validate(RawOrigin::Signed(controller).into(), validator_prefs)?; + let stash_lookup = T::Lookup::unlookup(stash); + validators.push(stash_lookup); } Ok(validators) @@ -227,13 +203,16 @@ where }; let (v_stash, v_controller) = create_stash_controller::(i, balance_factor, RewardDestination::Staked)?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), v_stash.did(), Some(2))?; + T::Permissioned::permission_validator(&v_stash); let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(v_controller.origin().into(), validator_prefs)?; - let stash_lookup = v_stash.lookup(); + Staking::::validate( + RawOrigin::Signed(v_controller.clone()).into(), + validator_prefs, + )?; + let stash_lookup = T::Lookup::unlookup(v_stash.clone()); validators_stash.push(stash_lookup.clone()); } @@ -260,7 +239,7 @@ where let validator = available_validators.remove(selected); selected_validators.push(validator); } - Staking::::nominate(n_controller.origin().into(), selected_validators)?; + Staking::::nominate(RawOrigin::Signed(n_controller).into(), selected_validators)?; } ValidatorCount::::put(validators); diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index 2377d56c79..2a36226f5f 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -2,8 +2,73 @@ use sp_runtime::{Deserialize, Serialize}; use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::dispatch::DispatchResult; +use frame_support::traits::Currency; use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; +use sp_runtime::{Perbill, RuntimeDebug}; + +use crate::{ActiveEraInfo, BalanceOf, Config}; + +/// A trait used by the staking pallet for permissioned staking. +/// +/// A permissioned Substrate network can be configured to allow only a set of +/// identities to participate in staking. This trait is used to define the +/// behavior of the staking pallet in such a network. +pub trait PermissionedStaking { + /// Permission a validator. + #[cfg(feature = "runtime-benchmarks")] + fn permission_validator(_who: &T::AccountId) {} + + /// Setup stash and controller. + #[cfg(feature = "runtime-benchmarks")] + fn setup_stash_and_controller(_stash: &T::AccountId, _controller: &T::AccountId) {} + + /// Check if amount is under the existential deposit. + fn reapable(amount: BalanceOf) -> bool { + amount < T::Currency::minimum_balance() + } + + /// On validate hook. + fn on_validate(_who: &T::AccountId, _commission: Perbill) -> DispatchResult { + Ok(()) + } + + /// On chill hook. + fn on_chill(_who: &T::AccountId) {} + + /// On nominate hook. + fn on_nominate(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// Is the validator still compliant? + fn is_validator_compliant(_who: &T::AccountId) -> bool { + true + } + + /// Is the nominator still compliant? + fn is_nominator_compliant(_who: &T::AccountId) -> bool { + true + } + + /// Schedule reward payouts. + fn schedule_payouts(_active_era: &ActiveEraInfo) {} + + /// Who should be slashed? + fn who_to_slash() -> Option { + Some(WhoToSlash::ValidatorAndNominator) + } + + /// Is slashing enabled? + fn is_slashing_enabled() -> bool { + Self::who_to_slash().is_some() + } + + /// Slash nominators? + fn slash_nominators() -> bool { + Self::who_to_slash() == Some(WhoToSlash::ValidatorAndNominator) + } +} /// Preference of an identity regarding validation. #[derive(Decode, Encode, RuntimeDebug, TypeInfo)] @@ -38,6 +103,17 @@ impl PermissionedIdentityPrefs { } } +/// Who should be slashed. +#[derive(Decode, Encode, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[derive(Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum WhoToSlash { + /// Allow validators but not nominators to get slashed. + Validator, + /// Allow both validators and nominators to get slashed. + ValidatorAndNominator, +} + /// Switch used to change the "victim" for slashing. Victims can be /// validators, both validators and nominators, or no-one. #[derive(Decode, Encode, MaxEncodedLen, RuntimeDebug, TypeInfo)] @@ -52,3 +128,13 @@ pub enum SlashingSwitch { #[default] None, } + +impl From for Option { + fn from(value: SlashingSwitch) -> Self { + match value { + SlashingSwitch::Validator => Some(WhoToSlash::Validator), + SlashingSwitch::ValidatorAndNominator => Some(WhoToSlash::ValidatorAndNominator), + SlashingSwitch::None => None, + } + } +}