Fix nomination pools unbonding logic (#11746)

* 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

* first working version

* bring back all tests

* ALL new tests work now

* cleanup

* make sure benchmarks and all work

* 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

* round of self-review, make arithmetic safe

* fix warn

* add migration code

* Fix doc

* add precision notes

* make arithmetic fallible

* fix node runtime

* a lot of precision tests and notes and stuff

* document MaxPOintsToBalance better

* :round of self-review

* fmt

* fix some comments

* new logic, some broken tests

* Check if after unbonding remaining balance is more or equal to MinJoinBond and is not zero

* incorporate nikos' work

* make it work again

* merge

* Fix all tests

* fix all tests

* some updates

* Add tests

* remove erroneoysly placed comment

* Try to make lint pass

* Try to make lint pass

* revamp the tests for unbond

* fix docs

* Fix proportional slashing logic

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

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

* Update frame/nomination-pools/src/tests.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>

* track poinst in migration

* fix

* fmt

* fix migration

* remove event read

* Apply suggestions from code review

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

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* remove log

* Update frame/staking/src/lib.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

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

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

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

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* update

* fmt

* fmt

* add one last test

* fmrt

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

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

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

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

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>
Co-authored-by: wirednkod <wirednkod@gmail.com>
This commit is contained in:
Kian Paimani
2022-07-14 10:57:59 +01:00
committed by GitHub
parent 4cacae72c5
commit a98010a889
4 changed files with 464 additions and 173 deletions
+384 -96
View File
@@ -538,13 +538,13 @@ mod join {
StakingMock::set_bonded_balance(Pools::create_bonded_account(1), max_points_to_balance);
// Cannot join a pool that isn't open
unsafe_set_state(123, PoolState::Blocked).unwrap();
unsafe_set_state(123, PoolState::Blocked);
assert_noop!(
Pools::join(Origin::signed(11), max_points_to_balance, 123),
Error::<Runtime>::NotOpen
);
unsafe_set_state(123, PoolState::Destroying).unwrap();
unsafe_set_state(123, PoolState::Destroying);
assert_noop!(
Pools::join(Origin::signed(11), max_points_to_balance, 123),
Error::<Runtime>::NotOpen
@@ -1824,7 +1824,8 @@ mod claim_payout {
fn rewards_are_rounded_down_depositor_collects_them() {
ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| {
// initial balance of 10.
assert_eq!(Balances::free_balance(&10), 5);
assert_eq!(Balances::free_balance(&10), 35);
assert_eq!(
Balances::free_balance(&default_reward_account()),
Balances::minimum_balance()
@@ -1875,7 +1876,7 @@ mod claim_payout {
);
// original ed + ed put into reward account + reward + bond + dust.
assert_eq!(Balances::free_balance(&10), 5 + 5 + 13 + 10 + 1);
assert_eq!(Balances::free_balance(&10), 35 + 5 + 13 + 10 + 1);
})
}
@@ -1942,10 +1943,269 @@ mod claim_payout {
mod unbond {
use super::*;
#[test]
fn member_unbond_open() {
// depositor in pool, pool state open
// - member unbond above limit
// - member unbonds to 0
// - member cannot unbond between within limit and 0
ExtBuilder::default()
.min_join_bond(10)
.add_members(vec![(20, 20)])
.build_and_execute(|| {
// can unbond to above limit
assert_ok!(Pools::unbond(Origin::signed(20), 20, 5));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().active_points(), 15);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_points(), 5);
// cannot go to below 10:
assert_noop!(
Pools::unbond(Origin::signed(20), 20, 10),
Error::<T>::MinimumBondNotMet
);
// but can go to 0
assert_ok!(Pools::unbond(Origin::signed(20), 20, 15));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().active_points(), 0);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_points(), 20);
})
}
#[test]
fn member_kicked() {
// depositor in pool, pool state blocked
// - member cannot be kicked to above limit
// - member cannot be kicked between within limit and 0
// - member kicked to 0
ExtBuilder::default()
.min_join_bond(10)
.add_members(vec![(20, 20)])
.build_and_execute(|| {
unsafe_set_state(1, PoolState::Blocked);
let kicker = DEFAULT_ROLES.state_toggler.unwrap();
// cannot be kicked to above the limit.
assert_noop!(
Pools::unbond(Origin::signed(kicker), 20, 5),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// cannot go to below 10:
assert_noop!(
Pools::unbond(Origin::signed(kicker), 20, 15),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// but they themselves can do an unbond
assert_ok!(Pools::unbond(Origin::signed(20), 20, 2));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().active_points(), 18);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_points(), 2);
// can be kicked to 0.
assert_ok!(Pools::unbond(Origin::signed(kicker), 20, 18));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().active_points(), 0);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_points(), 20);
})
}
#[test]
fn member_unbond_destroying() {
// depositor in pool, pool state destroying
// - member cannot be permissionlessly unbonded to above limit
// - member cannot be permissionlessly unbonded between within limit and 0
// - member permissionlessly unbonded to 0
ExtBuilder::default()
.min_join_bond(10)
.add_members(vec![(20, 20)])
.build_and_execute(|| {
unsafe_set_state(1, PoolState::Destroying);
let random = 123;
// cannot be kicked to above the limit.
assert_noop!(
Pools::unbond(Origin::signed(random), 20, 5),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// cannot go to below 10:
assert_noop!(
Pools::unbond(Origin::signed(random), 20, 15),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// but they themselves can do an unbond
assert_ok!(Pools::unbond(Origin::signed(20), 20, 2));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().active_points(), 18);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_points(), 2);
// but can go to 0
assert_ok!(Pools::unbond(Origin::signed(random), 20, 18));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().active_points(), 0);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_points(), 20);
})
}
#[test]
fn depositor_unbond_open() {
// depositor in pool, pool state open
// - depositor unbonds to above limit
// - depositor cannot unbond to below limit or 0
ExtBuilder::default().min_join_bond(10).build_and_execute(|| {
// give the depositor some extra funds.
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
assert_eq!(PoolMembers::<T>::get(10).unwrap().points, 20);
// can unbond to above the limit.
assert_ok!(Pools::unbond(Origin::signed(10), 10, 5));
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 15);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 5);
// cannot go to below 10:
assert_noop!(Pools::unbond(Origin::signed(10), 10, 10), Error::<T>::MinimumBondNotMet);
// cannot go to 0 either.
assert_noop!(Pools::unbond(Origin::signed(10), 10, 15), Error::<T>::MinimumBondNotMet);
})
}
#[test]
fn depositor_kick() {
// depositor in pool, pool state blocked
// - depositor can never be kicked.
ExtBuilder::default().min_join_bond(10).build_and_execute(|| {
// give the depositor some extra funds.
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
assert_eq!(PoolMembers::<T>::get(10).unwrap().points, 20);
// set the stage
unsafe_set_state(1, PoolState::Blocked);
let kicker = DEFAULT_ROLES.state_toggler.unwrap();
// cannot be kicked to above limit.
assert_noop!(
Pools::unbond(Origin::signed(kicker), 10, 5),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// or below the limit
assert_noop!(
Pools::unbond(Origin::signed(kicker), 10, 15),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// or 0.
assert_noop!(
Pools::unbond(Origin::signed(kicker), 10, 20),
Error::<T>::DoesNotHavePermission
);
// they themselves cannot do it either
assert_noop!(Pools::unbond(Origin::signed(10), 10, 20), Error::<T>::MinimumBondNotMet);
})
}
#[test]
fn depositor_unbond_destroying_permissionless() {
// depositor can never be permissionlessly unbonded.
ExtBuilder::default().min_join_bond(10).build_and_execute(|| {
// give the depositor some extra funds.
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
assert_eq!(PoolMembers::<T>::get(10).unwrap().points, 20);
// set the stage
unsafe_set_state(1, PoolState::Destroying);
let random = 123;
// cannot be kicked to above limit.
assert_noop!(
Pools::unbond(Origin::signed(random), 10, 5),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// or below the limit
assert_noop!(
Pools::unbond(Origin::signed(random), 10, 15),
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);
// or 0.
assert_noop!(
Pools::unbond(Origin::signed(random), 10, 20),
Error::<T>::DoesNotHavePermission
);
// they themselves can do it in this case though.
assert_ok!(Pools::unbond(Origin::signed(10), 10, 20));
})
}
#[test]
fn depositor_unbond_destroying_not_last_member() {
// deposit in pool, pool state destroying
// - depositor can never leave if there is another member in the pool.
ExtBuilder::default()
.min_join_bond(10)
.add_members(vec![(20, 20)])
.build_and_execute(|| {
// give the depositor some extra funds.
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
assert_eq!(PoolMembers::<T>::get(10).unwrap().points, 20);
// set the stage
unsafe_set_state(1, PoolState::Destroying);
// can go above the limit
assert_ok!(Pools::unbond(Origin::signed(10), 10, 5));
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 15);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 5);
// but not below the limit
assert_noop!(
Pools::unbond(Origin::signed(10), 10, 10),
Error::<T>::MinimumBondNotMet
);
// and certainly not zero
assert_noop!(
Pools::unbond(Origin::signed(10), 10, 15),
Error::<T>::MinimumBondNotMet
);
})
}
#[test]
fn depositor_unbond_destroying_last_member() {
// deposit in pool, pool state destroying
// - depositor can unbond to above limit always.
// - depositor cannot unbond to below limit if last.
// - depositor can unbond to 0 if last and destroying.
ExtBuilder::default().min_join_bond(10).build_and_execute(|| {
// give the depositor some extra funds.
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
assert_eq!(PoolMembers::<T>::get(10).unwrap().points, 20);
// set the stage
unsafe_set_state(1, PoolState::Destroying);
// can unbond to above the limit.
assert_ok!(Pools::unbond(Origin::signed(10), 10, 5));
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 15);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 5);
// still cannot go to below limit
assert_noop!(Pools::unbond(Origin::signed(10), 10, 10), Error::<T>::MinimumBondNotMet);
// can go to 0 too.
assert_ok!(Pools::unbond(Origin::signed(10), 10, 15));
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 0);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 20);
})
}
#[test]
fn unbond_of_1_works() {
ExtBuilder::default().build_and_execute(|| {
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
assert_ok!(fully_unbond_permissioned(10));
assert_eq!(
@@ -2021,7 +2281,7 @@ mod unbond {
assert_eq!(Balances::free_balance(&40), 40 + 40); // We claim rewards when unbonding
// When
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
assert_ok!(fully_unbond_permissioned(550));
// Then
@@ -2111,7 +2371,7 @@ mod unbond {
},
},
);
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
// When
let current_era = 1 + TotalUnbondingPools::<Runtime>::get();
@@ -2148,7 +2408,7 @@ mod unbond {
.add_members(vec![(100, 100), (200, 200)])
.build_and_execute(|| {
// Given
unsafe_set_state(1, PoolState::Blocked).unwrap();
unsafe_set_state(1, PoolState::Blocked);
let bonded_pool = BondedPool::<Runtime>::get(1).unwrap();
assert_eq!(bonded_pool.roles.root.unwrap(), 900);
assert_eq!(bonded_pool.roles.nominator.unwrap(), 901);
@@ -2216,7 +2476,7 @@ mod unbond {
// Scenarios where non-admin accounts can unbond others
ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| {
// Given the pool is blocked
unsafe_set_state(1, PoolState::Blocked).unwrap();
unsafe_set_state(1, PoolState::Blocked);
// A permissionless unbond attempt errors
assert_noop!(
@@ -2231,16 +2491,17 @@ mod unbond {
);
// Given the pool is destroying
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
// The depositor cannot be fully unbonded until they are the last member
assert_noop!(
Pools::fully_unbond(Origin::signed(10), 10),
Error::<Runtime>::NotOnlyPoolMember
Error::<Runtime>::MinimumBondNotMet,
);
// Any account can unbond a member that is not the depositor
assert_ok!(Pools::fully_unbond(Origin::signed(420), 100));
assert_eq!(
pool_events_since_last_call(),
vec![
@@ -2258,7 +2519,7 @@ mod unbond {
);
// Given the pool is blocked
unsafe_set_state(1, PoolState::Blocked).unwrap();
unsafe_set_state(1, PoolState::Blocked);
// The depositor cannot be unbonded
assert_noop!(
@@ -2267,7 +2528,7 @@ mod unbond {
);
// Given the pools is destroying
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
// The depositor cannot be unbonded yet.
assert_noop!(
@@ -2285,8 +2546,13 @@ mod unbond {
Error::<Runtime>::PartialUnbondNotAllowedPermissionlessly,
);
// but full unbond works.
assert_ok!(Pools::fully_unbond(Origin::signed(420), 10));
// depositor can never be unbonded permissionlessly .
assert_noop!(
Pools::fully_unbond(Origin::signed(420), 10),
Error::<T>::DoesNotHavePermission
);
// but depositor itself can do it.
assert_ok!(Pools::fully_unbond(Origin::signed(10), 10));
assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 0);
assert_eq!(
@@ -2346,6 +2612,12 @@ mod unbond {
#[test]
fn partial_unbond_era_tracking() {
ExtBuilder::default().build_and_execute(|| {
// to make the depositor capable of withdrawing.
StakingMinBond::set(1);
MinCreateBond::<T>::set(1);
MinJoinBond::<T>::set(1);
assert_eq!(Pools::depositor_min_bond(), 1);
// given
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 10);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 0);
@@ -2360,7 +2632,7 @@ mod unbond {
assert_eq!(BondingDuration::get(), 3);
// so the depositor can leave, just keeps the test simpler.
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
// when: casual unbond
assert_ok!(Pools::unbond(Origin::signed(10), 10, 1));
@@ -2444,13 +2716,13 @@ mod unbond {
);
// when: unbonding more than our active: error
assert_err!(
assert_noop!(
frame_support::storage::in_storage_layer(|| Pools::unbond(
Origin::signed(10),
10,
5
)),
Error::<Runtime>::NotEnoughPointsToUnbond
Error::<Runtime>::MinimumBondNotMet
);
// instead:
assert_ok!(Pools::unbond(Origin::signed(10), 10, 3));
@@ -2482,26 +2754,24 @@ mod unbond {
#[test]
fn partial_unbond_max_chunks() {
ExtBuilder::default().ed(1).build_and_execute(|| {
// so the depositor can leave, just keeps the test simpler.
unsafe_set_state(1, PoolState::Destroying).unwrap();
ExtBuilder::default().add_members(vec![(20, 20)]).ed(1).build_and_execute(|| {
MaxUnbonding::set(2);
// given
assert_ok!(Pools::unbond(Origin::signed(10), 10, 2));
assert_ok!(Pools::unbond(Origin::signed(20), 20, 2));
CurrentEra::set(1);
assert_ok!(Pools::unbond(Origin::signed(10), 10, 3));
assert_ok!(Pools::unbond(Origin::signed(20), 20, 3));
assert_eq!(
PoolMembers::<Runtime>::get(10).unwrap().unbonding_eras,
PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras,
member_unbonding_eras!(3 => 2, 4 => 3)
);
// when
CurrentEra::set(2);
assert_err!(
assert_noop!(
frame_support::storage::in_storage_layer(|| Pools::unbond(
Origin::signed(10),
10,
Origin::signed(20),
20,
4
)),
Error::<Runtime>::MaxUnbondingLimit
@@ -2509,30 +2779,35 @@ mod unbond {
// when
MaxUnbonding::set(3);
assert_ok!(Pools::unbond(Origin::signed(10), 10, 1));
assert_ok!(Pools::unbond(Origin::signed(20), 20, 1));
assert_eq!(
PoolMembers::<Runtime>::get(10).unwrap().unbonding_eras,
PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras,
member_unbonding_eras!(3 => 2, 4 => 3, 5 => 1)
);
assert_eq!(
pool_events_since_last_call(),
vec![
Event::Created { depositor: 10, pool_id: 1 },
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2 },
Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3 },
Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1 }
Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true },
Event::Unbonded { member: 20, pool_id: 1, balance: 2, points: 2 },
Event::Unbonded { member: 20, pool_id: 1, balance: 3, points: 3 },
Event::Unbonded { member: 20, pool_id: 1, balance: 1, points: 1 }
]
);
})
}
// depositor can unbond inly up to `MinCreateBond`.
// depositor can unbond only up to `MinCreateBond`.
#[test]
fn depositor_permissioned_partial_unbond() {
ExtBuilder::default().ed(1).build_and_execute(|| {
// given
assert_eq!(MinCreateBond::<Runtime>::get(), 2);
StakingMinBond::set(5);
assert_eq!(Pools::depositor_min_bond(), 5);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 10);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 0);
@@ -2544,7 +2819,7 @@ mod unbond {
// but not less than 2
assert_noop!(
Pools::unbond(Origin::signed(10), 10, 6),
Error::<Runtime>::NotOnlyPoolMember
Error::<Runtime>::MinimumBondNotMet
);
assert_eq!(
@@ -2558,7 +2833,6 @@ mod unbond {
});
}
// same as above, but the pool is slashed and therefore the depositor cannot partially unbond.
#[test]
fn depositor_permissioned_partial_unbond_slashed() {
ExtBuilder::default().ed(1).build_and_execute(|| {
@@ -2573,78 +2847,69 @@ mod unbond {
// cannot unbond even 7, because the value of shares is now less.
assert_noop!(
Pools::unbond(Origin::signed(10), 10, 7),
Error::<Runtime>::NotOnlyPoolMember
);
assert_eq!(
pool_events_since_last_call(),
vec![
Event::Created { depositor: 10, pool_id: 1 },
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
]
Error::<Runtime>::MinimumBondNotMet
);
});
}
#[test]
fn every_unbonding_triggers_payout() {
ExtBuilder::default().build_and_execute(|| {
let initial_reward_account = Balances::free_balance(Pools::create_reward_account(1));
ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| {
let initial_reward_account = Balances::free_balance(default_reward_account());
assert_eq!(initial_reward_account, Balances::minimum_balance());
assert_eq!(initial_reward_account, 5);
// set the pool to destroying so that depositor can leave.
unsafe_set_state(1, PoolState::Destroying).unwrap();
Balances::make_free_balance_be(
&Pools::create_reward_account(1),
2 * Balances::minimum_balance(),
&default_reward_account(),
4 * Balances::minimum_balance(),
);
assert_ok!(Pools::unbond(Origin::signed(10), 10, 2));
assert_ok!(Pools::unbond(Origin::signed(20), 20, 2));
assert_eq!(
pool_events_since_last_call(),
vec![
// 2/3 of ed, which is 20's share.
Event::Created { depositor: 10, pool_id: 1 },
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
// exactly equal to ed, all that can be claimed.
Event::PaidOut { member: 10, pool_id: 1, payout: 5 },
Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2 }
Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true },
Event::PaidOut { member: 20, pool_id: 1, payout: 10 },
Event::Unbonded { member: 20, pool_id: 1, balance: 2, points: 2 }
]
);
CurrentEra::set(1);
Balances::make_free_balance_be(
&Pools::create_reward_account(1),
2 * Balances::minimum_balance(),
&default_reward_account(),
4 * Balances::minimum_balance(),
);
assert_ok!(Pools::unbond(Origin::signed(10), 10, 3));
assert_ok!(Pools::unbond(Origin::signed(20), 20, 3));
assert_eq!(
pool_events_since_last_call(),
vec![
// exactly equal to ed, all that can be claimed.
Event::PaidOut { member: 10, pool_id: 1, payout: 5 },
Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3 }
// 2/3 of ed, which is 20's share.
Event::PaidOut { member: 20, pool_id: 1, payout: 6 },
Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3 }
]
);
CurrentEra::set(2);
Balances::make_free_balance_be(
&Pools::create_reward_account(1),
2 * Balances::minimum_balance(),
&default_reward_account(),
4 * Balances::minimum_balance(),
);
assert_ok!(Pools::unbond(Origin::signed(10), 10, 5));
assert_ok!(Pools::unbond(Origin::signed(20), 20, 5));
assert_eq!(
pool_events_since_last_call(),
vec![
Event::PaidOut { member: 10, pool_id: 1, payout: 5 },
Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5 }
Event::PaidOut { member: 20, pool_id: 1, payout: 3 },
Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5 }
]
);
assert_eq!(
PoolMembers::<Runtime>::get(10).unwrap().unbonding_eras,
PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras,
member_unbonding_eras!(3 => 2, 4 => 3, 5 => 5)
);
});
@@ -2801,7 +3066,7 @@ mod withdraw_unbonded {
);
// now, finally, the depositor can take out its share.
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
assert_ok!(fully_unbond_permissioned(10));
current_era += 3;
@@ -2911,7 +3176,7 @@ mod withdraw_unbonded {
assert!(SubPoolsStorage::<Runtime>::get(&1).unwrap().with_era.is_empty());
// now, finally, the depositor can take out its share.
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
assert_ok!(fully_unbond_permissioned(10));
// because everyone else has left, the points
@@ -2926,7 +3191,7 @@ mod withdraw_unbonded {
assert_ok!(Pools::withdraw_unbonded(Origin::signed(10), 10, 0));
// then
assert_eq!(Balances::free_balance(&10), 10 + 5);
assert_eq!(Balances::free_balance(&10), 10 + 35);
assert_eq!(Balances::free_balance(&default_bonded_account()), 0);
// in this test 10 also gets a fair share of the slash, because the slash was
@@ -2955,9 +3220,9 @@ mod withdraw_unbonded {
ExtBuilder::default().build_and_execute(|| {
// Given
assert_eq!(Balances::minimum_balance(), 5);
assert_eq!(Balances::free_balance(&10), 5);
assert_eq!(Balances::free_balance(&10), 35);
assert_eq!(Balances::free_balance(&default_bonded_account()), 10);
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
assert_ok!(Pools::fully_unbond(Origin::signed(10), 10));
// Simulate a slash that is not accounted for in the sub pools.
@@ -2974,7 +3239,7 @@ mod withdraw_unbonded {
assert_ok!(Pools::withdraw_unbonded(Origin::signed(10), 10, 0));
// Then
assert_eq!(Balances::free_balance(10), 10 + 5);
assert_eq!(Balances::free_balance(10), 10 + 35);
assert_eq!(Balances::free_balance(&default_bonded_account()), 0);
});
}
@@ -3054,7 +3319,7 @@ mod withdraw_unbonded {
);
// Given
unsafe_set_state(1, PoolState::Blocked).unwrap();
unsafe_set_state(1, PoolState::Blocked);
// Cannot kick as a nominator
assert_noop!(
@@ -3112,7 +3377,7 @@ mod withdraw_unbonded {
);
// Given
unsafe_set_state(1, PoolState::Destroying).unwrap();
unsafe_set_state(1, PoolState::Destroying);
// Can permissionlesly withdraw a member that is not the depositor
assert_ok!(Pools::withdraw_unbonded(Origin::signed(420), 100, 0));
@@ -3137,8 +3402,8 @@ mod withdraw_unbonded {
#[test]
fn partial_withdraw_unbonded_depositor() {
ExtBuilder::default().ed(1).build_and_execute(|| {
// so the depositor can leave, just keeps the test simpler.
unsafe_set_state(1, PoolState::Destroying).unwrap();
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
unsafe_set_state(1, PoolState::Destroying);
// given
assert_ok!(Pools::unbond(Origin::signed(10), 10, 6));
@@ -3158,13 +3423,14 @@ mod withdraw_unbonded {
}
}
);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 3);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().active_points(), 13);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().unbonding_points(), 7);
assert_eq!(
pool_events_since_last_call(),
vec![
Event::Created { depositor: 10, pool_id: 1 },
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false },
Event::Unbonded { member: 10, pool_id: 1, points: 6, balance: 6 },
Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1 }
]
@@ -3368,50 +3634,72 @@ mod withdraw_unbonded {
#[test]
fn full_multi_step_withdrawing_depositor() {
ExtBuilder::default().ed(1).build_and_execute(|| {
// given
// depositor now has 20, they can unbond to 10.
assert_eq!(Pools::depositor_min_bond(), 10);
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::FreeBalance(10)));
// now they can.
assert_ok!(Pools::unbond(Origin::signed(10), 10, 7));
// progress one era and unbond the leftover.
CurrentEra::set(1);
unsafe_set_state(1, PoolState::Destroying).unwrap();
assert_ok!(Pools::unbond(Origin::signed(10), 10, 3));
assert_eq!(
PoolMembers::<Runtime>::get(10).unwrap().unbonding_eras,
member_unbonding_eras!(3 => 7, 4 => 3)
);
// they can't unbond to a value below 10 other than 0..
assert_noop!(
Pools::withdraw_unbonded(Origin::signed(10), 10, 0),
Error::<Runtime>::CannotWithdrawAny
Pools::unbond(Origin::signed(10), 10, 5),
Error::<Runtime>::MinimumBondNotMet
);
// but not even full, because they pool is not yet destroying.
assert_noop!(
Pools::unbond(Origin::signed(10), 10, 10),
Error::<Runtime>::MinimumBondNotMet
);
// but now they can.
unsafe_set_state(1, PoolState::Destroying);
assert_noop!(
Pools::unbond(Origin::signed(10), 10, 5),
Error::<Runtime>::MinimumBondNotMet
);
assert_ok!(Pools::unbond(Origin::signed(10), 10, 10));
// now the 7 should be free.
CurrentEra::set(3);
assert_ok!(Pools::withdraw_unbonded(Origin::signed(10), 10, 0));
assert_eq!(
pool_events_since_last_call(),
vec![
Event::Created { depositor: 10, pool_id: 1 },
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
Event::Unbonded { member: 10, pool_id: 1, points: 7, balance: 7 },
Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3 },
Event::Withdrawn { member: 10, pool_id: 1, points: 7, balance: 7 }
Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false },
Event::Unbonded { member: 10, pool_id: 1, balance: 7, points: 7 },
Event::Unbonded { member: 10, pool_id: 1, balance: 3, points: 3 },
Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10 },
Event::Withdrawn { member: 10, pool_id: 1, balance: 7, points: 7 }
]
);
assert_eq!(
PoolMembers::<Runtime>::get(10).unwrap().unbonding_eras,
member_unbonding_eras!(4 => 3)
member_unbonding_eras!(4 => 13)
);
// the 25 should be free now, and the member removed.
// the 13 should be free now, and the member removed.
CurrentEra::set(4);
assert_ok!(Pools::withdraw_unbonded(Origin::signed(10), 10, 0));
assert_eq!(
pool_events_since_last_call(),
vec![
Event::Withdrawn { member: 10, pool_id: 1, points: 3, balance: 3 },
Event::Withdrawn { member: 10, pool_id: 1, points: 13, balance: 13 },
Event::MemberRemoved { pool_id: 1, member: 10 },
// the pool is also destroyed now.
Event::Destroyed { pool_id: 1 },
]
);
@@ -3640,7 +3928,7 @@ mod set_state {
// If the pool is not ok to be open, then anyone can set it to destroying
// Given
unsafe_set_state(1, PoolState::Open).unwrap();
unsafe_set_state(1, PoolState::Open);
let mut bonded_pool = BondedPool::<Runtime>::get(1).unwrap();
bonded_pool.points = 100;
bonded_pool.put();
@@ -3651,7 +3939,7 @@ mod set_state {
// Given
Balances::make_free_balance_be(&default_bonded_account(), Balance::max_value() / 10);
unsafe_set_state(1, PoolState::Open).unwrap();
unsafe_set_state(1, PoolState::Open);
// When
assert_ok!(Pools::set_state(Origin::signed(11), 1, PoolState::Destroying));
// Then
@@ -3659,7 +3947,7 @@ mod set_state {
// If the pool is not ok to be open, it cannot be permissionleslly set to a state that
// isn't destroying
unsafe_set_state(1, PoolState::Open).unwrap();
unsafe_set_state(1, PoolState::Open);
assert_noop!(
Pools::set_state(Origin::signed(11), 1, PoolState::Blocked),
Error::<Runtime>::CanNotChangeState
@@ -3820,13 +4108,13 @@ mod bond_extra {
// given
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10);
assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10);
assert_eq!(Balances::free_balance(10), 5);
assert_eq!(Balances::free_balance(10), 35);
// when
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::Rewards));
// then
assert_eq!(Balances::free_balance(10), 5);
assert_eq!(Balances::free_balance(10), 35);
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10 + claimable_reward);
assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10 + claimable_reward);
@@ -3862,14 +4150,14 @@ mod bond_extra {
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20);
assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30);
assert_eq!(Balances::free_balance(10), 5);
assert_eq!(Balances::free_balance(10), 35);
assert_eq!(Balances::free_balance(20), 20);
// when
assert_ok!(Pools::bond_extra(Origin::signed(10), BondExtra::Rewards));
// then
assert_eq!(Balances::free_balance(10), 5);
assert_eq!(Balances::free_balance(10), 35);
// 10's share of the reward is 1/3, since they gave 10/30 of the total shares.
assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10 + 1);
assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30 + 1);