mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
Allow nomination pools to chill + fix dismantle scenario (#11426)
* make pool roles optional * undo lock file changes? * add migration * add the ability for pools to chill themselves * boilerplate of tests * somewhat stable, but I think I found another bug as well * Fix it all * Add more more sophisticated test + capture one more bug. * Update frame/staking/src/lib.rs * reduce the diff a little bit * add some test for the slashing bug * cleanup * fix lock file? * Fix * fmt * Update frame/nomination-pools/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/nomination-pools/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/nomination-pools/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/nomination-pools/src/mock.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix build * fix some fishy tests.. * add one last integrity check for MinCreateBond * remove bad assertion -- needs to be dealt with later * nits * fix tests and add benchmarks for chill * remove stuff * fix benchmarks * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_nomination_pools --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/nomination-pools/src/weights.rs --template=./.maintain/frame-weight-template.hbs * remove defensive Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Parity Bot <admin@parity.io>
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
mod mock;
|
||||
|
||||
use frame_support::{assert_noop, assert_ok, bounded_btree_map, traits::Currency};
|
||||
use mock::*;
|
||||
use pallet_nomination_pools::{
|
||||
Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers, PoolState,
|
||||
};
|
||||
use pallet_staking::{CurrentEra, Event as StakingEvent, Payee, RewardDestination};
|
||||
|
||||
#[test]
|
||||
fn pool_lifecycle_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(Origin::signed(10), 50, 10, 10, 10));
|
||||
assert_eq!(LastPoolId::<Runtime>::get(), 1);
|
||||
|
||||
// have the pool nominate.
|
||||
assert_ok!(Pools::nominate(Origin::signed(10), 1, vec![1, 2, 3]));
|
||||
|
||||
assert_eq!(staking_events_since_last_call(), vec![StakingEvent::Bonded(POOL1_BONDED, 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(Origin::signed(20), 10, 1));
|
||||
assert_ok!(Pools::join(Origin::signed(21), 10, 1));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Bonded(POOL1_BONDED, 10), StakingEvent::Bonded(POOL1_BONDED, 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(Origin::signed(10), 1, PoolState::Destroying));
|
||||
|
||||
// depositor cannot unbond yet.
|
||||
assert_noop!(
|
||||
Pools::unbond(Origin::signed(10), 10, 50),
|
||||
PoolsError::<Runtime>::NotOnlyPoolMember,
|
||||
);
|
||||
|
||||
// now the members want to unbond.
|
||||
assert_ok!(Pools::unbond(Origin::signed(20), 20, 10));
|
||||
assert_ok!(Pools::unbond(Origin::signed(21), 21, 10));
|
||||
|
||||
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1);
|
||||
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0);
|
||||
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1);
|
||||
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0);
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 10),
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 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 },
|
||||
PoolsEvent::Unbonded { member: 21, pool_id: 1, points: 10, balance: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
// depositor cannot still unbond
|
||||
assert_noop!(
|
||||
Pools::unbond(Origin::signed(10), 10, 50),
|
||||
PoolsError::<Runtime>::NotOnlyPoolMember,
|
||||
);
|
||||
|
||||
for e in 1..BondingDuration::get() {
|
||||
CurrentEra::<Runtime>::set(Some(e));
|
||||
assert_noop!(
|
||||
Pools::withdraw_unbonded(Origin::signed(20), 20, 0),
|
||||
PoolsError::<Runtime>::CannotWithdrawAny
|
||||
);
|
||||
}
|
||||
|
||||
// members are now unlocked.
|
||||
CurrentEra::<Runtime>::set(Some(BondingDuration::get()));
|
||||
|
||||
// depositor cannot still unbond
|
||||
assert_noop!(
|
||||
Pools::unbond(Origin::signed(10), 10, 50),
|
||||
PoolsError::<Runtime>::NotOnlyPoolMember,
|
||||
);
|
||||
|
||||
// but members can now withdraw.
|
||||
assert_ok!(Pools::withdraw_unbonded(Origin::signed(20), 20, 0));
|
||||
assert_ok!(Pools::withdraw_unbonded(Origin::signed(21), 21, 0));
|
||||
assert!(PoolMembers::<Runtime>::get(20).is_none());
|
||||
assert!(PoolMembers::<Runtime>::get(21).is_none());
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Withdrawn(POOL1_BONDED, 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(Origin::signed(10), 10, 50),
|
||||
pallet_staking::Error::<Runtime>::InsufficientBond
|
||||
);
|
||||
|
||||
assert_ok!(Pools::chill(Origin::signed(10), 1));
|
||||
assert_ok!(Pools::unbond(Origin::signed(10), 10, 50));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Chilled(POOL1_BONDED), StakingEvent::Unbonded(POOL1_BONDED, 50),]
|
||||
);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 50, balance: 50 }]
|
||||
);
|
||||
|
||||
// waiting another bonding duration:
|
||||
CurrentEra::<Runtime>::set(Some(BondingDuration::get() * 2));
|
||||
assert_ok!(Pools::withdraw_unbonded(Origin::signed(10), 10, 1));
|
||||
|
||||
// pools is fully destroyed now.
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Withdrawn(POOL1_BONDED, 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(|| {
|
||||
ExistentialDeposit::set(1);
|
||||
assert_eq!(Balances::minimum_balance(), 1);
|
||||
assert_eq!(Staking::current_era(), None);
|
||||
|
||||
// create the pool, we know this has id 1.
|
||||
assert_ok!(Pools::create(Origin::signed(10), 40, 10, 10, 10));
|
||||
assert_eq!(LastPoolId::<Runtime>::get(), 1);
|
||||
|
||||
assert_eq!(staking_events_since_last_call(), vec![StakingEvent::Bonded(POOL1_BONDED, 40)]);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
PoolsEvent::Created { depositor: 10, pool_id: 1 },
|
||||
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(Payee::<Runtime>::get(POOL1_BONDED), RewardDestination::Account(POOL1_REWARD));
|
||||
|
||||
// have two members join
|
||||
assert_ok!(Pools::join(Origin::signed(20), 20, 1));
|
||||
assert_ok!(Pools::join(Origin::signed(21), 20, 1));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Bonded(POOL1_BONDED, 20), StakingEvent::Bonded(POOL1_BONDED, 20)]
|
||||
);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true },
|
||||
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 20, joined: true },
|
||||
]
|
||||
);
|
||||
|
||||
// now let's progress a bit.
|
||||
CurrentEra::<Runtime>::set(Some(1));
|
||||
|
||||
// 20 / 80 of the total funds are unlocked, and safe from any further slash.
|
||||
assert_ok!(Pools::unbond(Origin::signed(10), 10, 10));
|
||||
assert_ok!(Pools::unbond(Origin::signed(20), 20, 10));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 10),
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 10)
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10 },
|
||||
PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10 }
|
||||
]
|
||||
);
|
||||
|
||||
CurrentEra::<Runtime>::set(Some(2));
|
||||
|
||||
// note: depositor cannot fully unbond at this point.
|
||||
// these funds will still get slashed.
|
||||
assert_ok!(Pools::unbond(Origin::signed(10), 10, 10));
|
||||
assert_ok!(Pools::unbond(Origin::signed(20), 20, 10));
|
||||
assert_ok!(Pools::unbond(Origin::signed(21), 21, 10));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 10),
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 10),
|
||||
StakingEvent::Unbonded(POOL1_BONDED, 10),
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10 },
|
||||
PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10 },
|
||||
PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 10, points: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
// At this point, 20 are safe from slash, 30 are unlocking but vulnerable to slash, and and
|
||||
// another 30 are active and vulnerable to slash. Let's slash half of them.
|
||||
pallet_staking::slashing::do_slash::<Runtime>(
|
||||
&POOL1_BONDED,
|
||||
30,
|
||||
&mut Default::default(),
|
||||
&mut Default::default(),
|
||||
1, // slash era 1, affects chunks at era 5 onwards.
|
||||
);
|
||||
|
||||
assert_eq!(staking_events_since_last_call(), vec![StakingEvent::Slashed(POOL1_BONDED, 30)]);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
// 30 has been slashed to 15 (15 slash)
|
||||
PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 15 },
|
||||
// 30 has been slashed to 15 (15 slash)
|
||||
PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 }
|
||||
]
|
||||
);
|
||||
|
||||
CurrentEra::<Runtime>::set(Some(3));
|
||||
assert_ok!(Pools::unbond(Origin::signed(21), 21, 10));
|
||||
|
||||
assert_eq!(
|
||||
PoolMembers::<Runtime>::get(21).unwrap(),
|
||||
PoolMember {
|
||||
pool_id: 1,
|
||||
points: 0,
|
||||
reward_pool_total_earnings: 0,
|
||||
// the 10 points unlocked just now correspond to 5 points in the unbond pool.
|
||||
unbonding_eras: bounded_btree_map!(5 => 10, 6 => 5)
|
||||
}
|
||||
);
|
||||
assert_eq!(staking_events_since_last_call(), vec![StakingEvent::Unbonded(POOL1_BONDED, 5)]);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 5, points: 5 }]
|
||||
);
|
||||
|
||||
// now we start withdrawing. we do it all at once, at era 6 where 20 and 21 are fully free.
|
||||
CurrentEra::<Runtime>::set(Some(6));
|
||||
assert_ok!(Pools::withdraw_unbonded(Origin::signed(20), 20, 0));
|
||||
assert_ok!(Pools::withdraw_unbonded(Origin::signed(21), 21, 0));
|
||||
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
// 20 had unbonded 10 safely, and 10 got slashed by half.
|
||||
PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 },
|
||||
PoolsEvent::MemberRemoved { pool_id: 1, member: 20 },
|
||||
// 21 unbonded all of it after the slash
|
||||
PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 },
|
||||
PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
// a 10 (un-slashed) + 10/2 (slashed) balance from 10 has also been unlocked
|
||||
vec![StakingEvent::Withdrawn(POOL1_BONDED, 15 + 10 + 15)]
|
||||
);
|
||||
|
||||
// now, finally, we can unbond the depositor further than their current limit.
|
||||
assert_ok!(Pools::set_state(Origin::signed(10), 1, PoolState::Destroying));
|
||||
assert_ok!(Pools::unbond(Origin::signed(10), 10, 20));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Unbonded(POOL1_BONDED, 10)]
|
||||
);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },
|
||||
PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 10, balance: 10 }
|
||||
]
|
||||
);
|
||||
|
||||
CurrentEra::<Runtime>::set(Some(9));
|
||||
assert_eq!(
|
||||
PoolMembers::<Runtime>::get(10).unwrap(),
|
||||
PoolMember {
|
||||
pool_id: 1,
|
||||
points: 0,
|
||||
reward_pool_total_earnings: 0,
|
||||
unbonding_eras: bounded_btree_map!(4 => 10, 5 => 10, 9 => 10)
|
||||
}
|
||||
);
|
||||
// withdraw the depositor, they should lose 12 balance in total due to slash.
|
||||
assert_ok!(Pools::withdraw_unbonded(Origin::signed(10), 10, 0));
|
||||
|
||||
assert_eq!(
|
||||
staking_events_since_last_call(),
|
||||
vec![StakingEvent::Withdrawn(POOL1_BONDED, 10)]
|
||||
);
|
||||
assert_eq!(
|
||||
pool_events_since_last_call(),
|
||||
vec![
|
||||
PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 },
|
||||
PoolsEvent::MemberRemoved { pool_id: 1, member: 10 },
|
||||
PoolsEvent::Destroyed { pool_id: 1 }
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use frame_election_provider_support::VoteWeight;
|
||||
use frame_support::{assert_ok, pallet_prelude::*, parameter_types, traits::ConstU64, PalletId};
|
||||
use sp_runtime::traits::{Convert, IdentityLookup};
|
||||
|
||||
type AccountId = u128;
|
||||
type AccountIndex = u32;
|
||||
type BlockNumber = u64;
|
||||
type Balance = u128;
|
||||
|
||||
pub(crate) const POOL1_BONDED: AccountId = 20318131474730217858575332831085u128;
|
||||
pub(crate) const POOL1_REWARD: AccountId = 20397359637244482196168876781421u128;
|
||||
|
||||
impl frame_system::Config for Runtime {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type Origin = Origin;
|
||||
type Index = AccountIndex;
|
||||
type BlockNumber = BlockNumber;
|
||||
type Call = Call;
|
||||
type Hash = sp_core::H256;
|
||||
type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = sp_runtime::testing::Header;
|
||||
type Event = Event;
|
||||
type BlockHashCount = ();
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Config for Runtime {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
type MinimumPeriod = ConstU64<5>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static ExistentialDeposit: Balance = 5;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Runtime {
|
||||
type MaxLocks = ();
|
||||
type MaxReserves = ();
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
type Balance = Balance;
|
||||
type Event = Event;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
pallet_staking_reward_curve::build! {
|
||||
const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!(
|
||||
min_inflation: 0_025_000,
|
||||
max_inflation: 0_100_000,
|
||||
ideal_stake: 0_500_000,
|
||||
falloff: 0_050_000,
|
||||
max_piece_count: 40,
|
||||
test_precision: 0_005_000,
|
||||
);
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS;
|
||||
pub static BondingDuration: u32 = 3;
|
||||
}
|
||||
|
||||
impl pallet_staking::Config for Runtime {
|
||||
type MaxNominations = ConstU32<16>;
|
||||
type Currency = Balances;
|
||||
type CurrencyBalance = Balance;
|
||||
type UnixTime = pallet_timestamp::Pallet<Self>;
|
||||
type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote;
|
||||
type RewardRemainder = ();
|
||||
type Event = Event;
|
||||
type Slash = ();
|
||||
type Reward = ();
|
||||
type SessionsPerEra = ();
|
||||
type SlashDeferDuration = ();
|
||||
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
|
||||
type BondingDuration = BondingDuration;
|
||||
type SessionInterface = ();
|
||||
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
|
||||
type NextNewSession = ();
|
||||
type MaxNominatorRewardedPerValidator = ConstU32<64>;
|
||||
type OffendingValidatorsThreshold = ();
|
||||
type ElectionProvider =
|
||||
frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking)>;
|
||||
type GenesisElectionProvider = Self::ElectionProvider;
|
||||
type VoterList = pallet_bags_list::Pallet<Self>;
|
||||
type MaxUnlockingChunks = ConstU32<32>;
|
||||
type OnStakerSlash = Pools;
|
||||
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static BagThresholds: &'static [VoteWeight] = &[10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000];
|
||||
}
|
||||
|
||||
impl pallet_bags_list::Config for Runtime {
|
||||
type Event = Event;
|
||||
type WeightInfo = ();
|
||||
type BagThresholds = BagThresholds;
|
||||
type ScoreProvider = Staking;
|
||||
type Score = VoteWeight;
|
||||
}
|
||||
|
||||
pub struct BalanceToU256;
|
||||
impl Convert<Balance, sp_core::U256> for BalanceToU256 {
|
||||
fn convert(n: Balance) -> sp_core::U256 {
|
||||
n.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct U256ToBalance;
|
||||
impl Convert<sp_core::U256, Balance> for U256ToBalance {
|
||||
fn convert(n: sp_core::U256) -> Balance {
|
||||
n.try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const PostUnbondingPoolsWindow: u32 = 10;
|
||||
pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
|
||||
}
|
||||
|
||||
impl pallet_nomination_pools::Config for Runtime {
|
||||
type Event = Event;
|
||||
type WeightInfo = ();
|
||||
type Currency = Balances;
|
||||
type BalanceToU256 = BalanceToU256;
|
||||
type U256ToBalance = U256ToBalance;
|
||||
type StakingInterface = Staking;
|
||||
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
|
||||
type MaxMetadataLen = ConstU32<256>;
|
||||
type MaxUnbonding = ConstU32<8>;
|
||||
type MinPointsToBalance = ConstU32<10>;
|
||||
type PalletId = PoolsPalletId;
|
||||
}
|
||||
|
||||
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
|
||||
where
|
||||
Call: From<LocalCall>,
|
||||
{
|
||||
type OverarchingCall = Call;
|
||||
type Extrinsic = UncheckedExtrinsic;
|
||||
}
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Runtime>;
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Runtime where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: frame_system::{Pallet, Call, Event<T>},
|
||||
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
|
||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
|
||||
Staking: pallet_staking::{Pallet, Call, Config<T>, Storage, Event<T>},
|
||||
BagsList: pallet_bags_list::{Pallet, Call, Storage, Event<T>},
|
||||
Pools: pallet_nomination_pools::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut storage = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
|
||||
let _ = pallet_nomination_pools::GenesisConfig::<Runtime> {
|
||||
min_join_bond: 2,
|
||||
min_create_bond: 2,
|
||||
max_pools: Some(3),
|
||||
max_members_per_pool: Some(3),
|
||||
max_members: Some(3 * 3),
|
||||
}
|
||||
.assimilate_storage(&mut storage)
|
||||
.unwrap();
|
||||
|
||||
let _ = pallet_balances::GenesisConfig::<Runtime> {
|
||||
balances: vec![(10, 100), (20, 100), (21, 100), (22, 100)],
|
||||
}
|
||||
.assimilate_storage(&mut storage)
|
||||
.unwrap();
|
||||
|
||||
let mut ext = sp_io::TestExternalities::from(storage);
|
||||
|
||||
ext.execute_with(|| {
|
||||
// for events to be deposited.
|
||||
frame_system::Pallet::<Runtime>::set_block_number(1);
|
||||
|
||||
// set some limit for nominations.
|
||||
assert_ok!(Staking::set_staking_configs(
|
||||
Origin::root(),
|
||||
pallet_staking::ConfigOp::Set(10), // minimum nominator bond
|
||||
pallet_staking::ConfigOp::Noop,
|
||||
pallet_staking::ConfigOp::Noop,
|
||||
pallet_staking::ConfigOp::Noop,
|
||||
pallet_staking::ConfigOp::Noop,
|
||||
pallet_staking::ConfigOp::Noop,
|
||||
));
|
||||
});
|
||||
|
||||
ext
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
static ObservedEventsPools: usize = 0;
|
||||
static ObservedEventsStaking: usize = 0;
|
||||
static ObservedEventsBalances: usize = 0;
|
||||
}
|
||||
|
||||
pub(crate) fn pool_events_since_last_call() -> Vec<pallet_nomination_pools::Event<Runtime>> {
|
||||
let events = System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.filter_map(|e| if let Event::Pools(inner) = e { Some(inner) } else { None })
|
||||
.collect::<Vec<_>>();
|
||||
let already_seen = ObservedEventsPools::get();
|
||||
ObservedEventsPools::set(events.len());
|
||||
events.into_iter().skip(already_seen).collect()
|
||||
}
|
||||
|
||||
pub(crate) fn staking_events_since_last_call() -> Vec<pallet_staking::Event<Runtime>> {
|
||||
let events = System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.filter_map(|e| if let Event::Staking(inner) = e { Some(inner) } else { None })
|
||||
.collect::<Vec<_>>();
|
||||
let already_seen = ObservedEventsStaking::get();
|
||||
ObservedEventsStaking::set(events.len());
|
||||
events.into_iter().skip(already_seen).collect()
|
||||
}
|
||||
Reference in New Issue
Block a user