Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Fix nomination pools unbonding logic #11746

Merged
merged 115 commits into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
ea30833
make pool roles optional
kianenigma May 13, 2022
00e8c87
undo lock file changes?
kianenigma May 13, 2022
fa5e95a
add migration
kianenigma May 13, 2022
f2df79e
add the ability for pools to chill themselves
kianenigma May 14, 2022
56cf997
boilerplate of tests
kianenigma May 14, 2022
508bc0a
new system works, tests need fixing
kianenigma May 15, 2022
f83944a
somewhat stable, but I think I found another bug as well
kianenigma May 15, 2022
ac48c37
Fix it all
kianenigma May 16, 2022
cbeb9fb
Master.into()
kianenigma May 16, 2022
031040f
Add more more sophisticated test + capture one more bug.
kianenigma May 16, 2022
efc7b4f
Update frame/staking/src/lib.rs
kianenigma May 16, 2022
60d42f1
reduce the diff a little bit
kianenigma May 17, 2022
02aa7d4
add some test for the slashing bug
kianenigma May 19, 2022
90db26e
cleanup
kianenigma May 19, 2022
5bf6d9c
Merge branch 'kiz-pool-chill' of github.com:paritytech/substrate into…
kianenigma May 19, 2022
1d8c940
Master.into()
kianenigma May 19, 2022
2ec4857
Merge branch 'master' of github.com:paritytech/substrate into kiz-poo…
kianenigma May 20, 2022
a51e408
fix lock file?
kianenigma May 20, 2022
28c8852
Fix
kianenigma May 20, 2022
c9413a2
fmt
kianenigma May 20, 2022
433476d
Merge branch 'master' of github.com:paritytech/substrate into kiz-poo…
kianenigma May 23, 2022
c34b655
Update frame/nomination-pools/src/lib.rs
kianenigma May 23, 2022
d0d75a1
Update frame/nomination-pools/src/lib.rs
kianenigma May 23, 2022
a6afb06
Update frame/nomination-pools/src/lib.rs
kianenigma May 23, 2022
a3a43e7
Update frame/nomination-pools/src/mock.rs
kianenigma May 23, 2022
4b7b0c7
Fix build
kianenigma May 23, 2022
3f66688
Merge branch 'kiz-pool-chill' of github.com:paritytech/substrate into…
kianenigma May 23, 2022
640ec31
fix some fishy tests..
kianenigma May 23, 2022
398ddfe
add one last integrity check for MinCreateBond
kianenigma May 24, 2022
d5dc697
remove bad assertion -- needs to be dealt with later
kianenigma May 24, 2022
05fb517
Merge branch 'master' of github.com:paritytech/substrate into kiz-poo…
kianenigma May 24, 2022
f4dbd0a
nits
shawntabrizi May 25, 2022
0a79c80
Master.into()
kianenigma Jun 8, 2022
78c0310
Merge branch 'kiz-pool-chill' of github.com:paritytech/substrate into…
kianenigma Jun 9, 2022
0fb1125
fix tests and add benchmarks for chill
kianenigma Jun 9, 2022
12773bb
remove stuff
kianenigma Jun 9, 2022
ca475df
fix benchmarks
kianenigma Jun 10, 2022
44a2722
Merge branch 'master' of https://github.com/paritytech/substrate into…
Jun 10, 2022
f027faf
cargo run --quiet --profile=production --features=runtime-benchmarks…
Jun 10, 2022
33b581c
Master.into()
kianenigma Jun 11, 2022
c77613f
Merge branch 'kiz-pool-chill' of github.com:paritytech/substrate into…
kianenigma Jun 11, 2022
d69af2c
remove defensive
kianenigma Jun 11, 2022
d318197
Merge branch 'master' of github.com:paritytech/substrate into kiz-poo…
kianenigma Jun 13, 2022
486a0e9
first working version
kianenigma Jun 13, 2022
9b2113f
Master.into()
kianenigma Jun 14, 2022
36cb484
bring back all tests
kianenigma Jun 14, 2022
723574b
ALL new tests work now
kianenigma Jun 14, 2022
624abe9
Merge branch 'master' of github.com:paritytech/substrate into kiz-fix…
kianenigma Jun 14, 2022
82287b0
cleanup
kianenigma Jun 14, 2022
e403fb1
make sure benchmarks and all work
kianenigma Jun 15, 2022
4cad93a
Merge branch 'master' of https://github.com/paritytech/substrate into…
Jun 15, 2022
221369b
cargo run --quiet --profile=production --features=runtime-benchmarks…
Jun 15, 2022
696a55e
round of self-review, make arithmetic safe
kianenigma Jun 15, 2022
0689b58
Merge branch 'kiz-fix-reward-pools-2' of github.com:paritytech/substr…
kianenigma Jun 15, 2022
bcb413c
fix warn
kianenigma Jun 15, 2022
bce40f7
add migration code
kianenigma Jun 16, 2022
8fc25ac
Merge branch 'master' of github.com:paritytech/substrate into kiz-fix…
kianenigma Jun 16, 2022
fc3ad18
Fix doc
kianenigma Jun 17, 2022
03107f3
Merge branches 'kiz-fix-reward-pools-2' and 'master' of github.com:pa…
kianenigma Jun 21, 2022
9f875a9
add precision notes
kianenigma Jun 21, 2022
0513284
make arithmetic fallible
kianenigma Jun 21, 2022
1c43f09
fix node runtime
kianenigma Jun 21, 2022
ef56db6
a lot of precision tests and notes and stuff
kianenigma Jun 22, 2022
3da2364
document MaxPOintsToBalance better
kianenigma Jun 22, 2022
ed5083f
Merge branch 'master' of github.com:paritytech/substrate into kiz-fix…
kianenigma Jun 23, 2022
3690489
:round of self-review
kianenigma Jun 23, 2022
62d35c8
fmt
kianenigma Jun 23, 2022
1c840b2
fix some comments
kianenigma Jun 23, 2022
7d9d403
Merge branch 'master' of github.com:paritytech/substrate into kiz-fix…
kianenigma Jun 26, 2022
c9478e1
new logic, some broken tests
kianenigma Jun 25, 2022
bab0a38
Check if after unbonding remaining balance is more or equal to MinJoi…
wirednkod Jun 17, 2022
2a58b9e
incorporate nikos' work
kianenigma Jun 25, 2022
9f6c1cc
make it work again
kianenigma Jun 25, 2022
1741545
merge
kianenigma Jun 26, 2022
e838847
Fix all tests
kianenigma Jun 26, 2022
f5baafc
fix all tests
kianenigma Jun 26, 2022
b46234e
some updates
kianenigma Jun 27, 2022
d67ccaa
Add tests
wirednkod Jun 28, 2022
576686e
remove erroneoysly placed comment
wirednkod Jun 28, 2022
d2a43a2
Try to make lint pass
wirednkod Jun 29, 2022
e9e26d6
Try to make lint pass
wirednkod Jun 29, 2022
b306671
revamp the tests for unbond
kianenigma Jul 1, 2022
5c2309a
fix docs
kianenigma Jul 1, 2022
78b79f2
Master.into()
kianenigma Jul 1, 2022
a8ccd71
Fix proportional slashing logic
kianenigma Jul 5, 2022
60b7641
Update frame/nomination-pools/src/tests.rs
kianenigma Jul 6, 2022
a2082cd
Update frame/nomination-pools/src/tests.rs
kianenigma Jul 6, 2022
ecb7890
Update frame/nomination-pools/src/lib.rs
kianenigma Jul 6, 2022
7e56e1a
track poinst in migration
kianenigma Jul 6, 2022
80b31c0
Merge branch 'kiz-fix-reward-pools-2' of github.com:paritytech/substr…
kianenigma Jul 6, 2022
579da37
fix
kianenigma Jul 6, 2022
51c1608
fmt
kianenigma Jul 6, 2022
3779081
fix migration
kianenigma Jul 6, 2022
9171a13
remove event read
kianenigma Jul 6, 2022
b9ab747
Apply suggestions from code review
kianenigma Jul 7, 2022
e04dffd
Update frame/nomination-pools/src/lib.rs
kianenigma Jul 9, 2022
996e812
remove log
kianenigma Jul 9, 2022
d4f45e7
Update frame/staking/src/lib.rs
kianenigma Jul 9, 2022
ca47b05
Update frame/nomination-pools/src/lib.rs
kianenigma Jul 9, 2022
f3e10a9
Update frame/nomination-pools/src/lib.rs
kianenigma Jul 9, 2022
d6ee1d0
Merge branch 'kiz-fix-reward-pools-2' of github.com:paritytech/substr…
kianenigma Jul 10, 2022
463ddfd
Merge branch 'master' of github.com:paritytech/substrate into kiz-fix…
kianenigma Jul 10, 2022
51873c7
Merge branch 'kiz-fix-reward-pools-2' of github.com:paritytech/substr…
kianenigma Jul 10, 2022
a04ee21
Merge branch 'kiz-fix-reward-pools-2' of github.com:paritytech/substr…
kianenigma Jul 10, 2022
c205ac3
update
kianenigma Jul 10, 2022
69d1f9c
fmt
kianenigma Jul 10, 2022
3efe9f5
fmt
kianenigma Jul 10, 2022
84a2639
add one last test
kianenigma Jul 10, 2022
5c78b5b
Merge branch 'kiz-fix-reward-pools-2' of github.com:paritytech/substr…
kianenigma Jul 10, 2022
780c26d
fmrt
kianenigma Jul 10, 2022
810b64b
Master.into()
kianenigma Jul 13, 2022
6784824
Update frame/nomination-pools/src/lib.rs
kianenigma Jul 14, 2022
81517b7
Merge branch 'master' of github.com:paritytech/substrate into kiz-poo…
kianenigma Jul 14, 2022
62fab53
Update frame/nomination-pools/src/tests.rs
kianenigma Jul 14, 2022
383b946
Merge branch 'kiz-pools-unbonding-revamp-2' of github.com:paritytech/…
kianenigma Jul 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 65 additions & 70 deletions frame/nomination-pools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ impl<T: Config> PoolMember<T> {
self.points = new_points;
Ok(())
} else {
Err(Error::<T>::NotEnoughPointsToUnbond)
Err(Error::<T>::MinimumBondNotMet)
}
}

Expand Down Expand Up @@ -781,45 +781,60 @@ impl<T: Config> BondedPool<T> {
let is_depositor = *target_account == self.roles.depositor;
let is_full_unbond = unbonding_points == target_member.active_points();

let balance_after_unbond = {
let new_depositor_points =
target_member.active_points().saturating_sub(unbonding_points);
let mut target_member_after_unbond = (*target_member).clone();
target_member_after_unbond.points = new_depositor_points;
target_member_after_unbond.active_balance()
};

// any partial unbonding is only ever allowed if this unbond is permissioned.
ensure!(
is_permissioned || is_full_unbond,
Error::<T>::PartialUnbondNotAllowedPermissionlessly
);

// any unbond must comply with the balance condition:
ensure!(
is_full_unbond ||
balance_after_unbond >=
if is_depositor {
Pallet::<T>::depositor_min_bond()
} else {
MinJoinBond::<T>::get()
},
Error::<T>::MinimumBondNotMet
);

// additional checks:
match (is_permissioned, is_depositor) {
// If the pool is blocked, then an admin with kicking permissions can remove a
// member. If the pool is being destroyed, anyone can remove a member
(true, false) => (),
(true, true) => {
// permission depositor unbond: if destroying and pool is empty, always allowed,
// with no additional limits.
if self.is_destroying_and_only_depositor(target_member.active_points()) {
// everything good, let them unbond anything.
} else {
// depositor cannot fully unbond yet.
ensure!(!is_full_unbond, Error::<T>::MinimumBondNotMet);
}
},
(false, false) => {
// If the pool is blocked, then an admin with kicking permissions can remove a
// member. If the pool is being destroyed, anyone can remove a member
debug_assert!(is_full_unbond);
ensure!(
self.can_kick(caller) || self.is_destroying(),
Error::<T>::NotKickerOrDestroying
)
},
// Any member who is not the depositor can always unbond themselves
(true, false) => (),
(_, true) => {
if self.is_destroying_and_only_depositor(target_member.active_points()) {
// if the pool is about to be destroyed, anyone can unbond the depositor, and
// they can fully unbond.
} else {
// only the depositor can partially unbond, and they can only unbond up to the
// threshold.
ensure!(is_permissioned, Error::<T>::DoesNotHavePermission);
let balance_after_unbond = {
let new_depositor_points =
target_member.active_points().saturating_sub(unbonding_points);
let mut depositor_after_unbond = (*target_member).clone();
depositor_after_unbond.points = new_depositor_points;
depositor_after_unbond.active_balance()
};
ensure!(
balance_after_unbond >= MinCreateBond::<T>::get(),
Error::<T>::NotOnlyPoolMember
);
}
(false, true) => {
// the depositor can simply not be unbonded permissionlessly, period.
return Err(Error::<T>::DoesNotHavePermission.into())
},
};

Ok(())
}

Expand All @@ -830,25 +845,14 @@ impl<T: Config> BondedPool<T> {
&self,
caller: &T::AccountId,
target_account: &T::AccountId,
target_member: &PoolMember<T>,
sub_pools: &SubPools<T>,
) -> Result<(), DispatchError> {
if *target_account == self.roles.depositor {
ensure!(
sub_pools.sum_unbonding_points() == target_member.unbonding_points(),
Error::<T>::NotOnlyPoolMember
);
debug_assert_eq!(self.member_counter, 1, "only member must exist at this point");
Ok(())
} else {
// This isn't a depositor
let is_permissioned = caller == target_account;
ensure!(
is_permissioned || self.can_kick(caller) || self.is_destroying(),
Error::<T>::NotKickerOrDestroying
);
Ok(())
}
// This isn't a depositor
let is_permissioned = caller == target_account;
ensure!(
is_permissioned || self.can_kick(caller) || self.is_destroying(),
Error::<T>::NotKickerOrDestroying
);
Ok(())
}

/// Bond exactly `amount` from `who`'s funds into this pool.
Expand Down Expand Up @@ -1100,15 +1104,6 @@ impl<T: Config> SubPools<T> {
self
}

/// The sum of all unbonding points, regardless of whether they are actually unlocked or not.
fn sum_unbonding_points(&self) -> BalanceOf<T> {
self.no_era.points.saturating_add(
self.with_era
.values()
.fold(BalanceOf::<T>::zero(), |acc, pool| acc.saturating_add(pool.points)),
)
}

/// The sum of all unbonding balance, regardless of whether they are actually unlocked or not.
#[cfg(any(test, debug_assertions))]
fn sum_unbonding_balance(&self) -> BalanceOf<T> {
Expand Down Expand Up @@ -1419,15 +1414,16 @@ pub mod pallet {
/// None of the funds can be withdrawn yet because the bonding duration has not passed.
CannotWithdrawAny,
/// The amount does not meet the minimum bond to either join or create a pool.
///
/// The depositor can never unbond to a value less than
/// `Pallet::depositor_min_bond`. The caller does not have nominating
/// permissions for the pool. Members can never unbond to a value below `MinJoinBond`.
MinimumBondNotMet,
/// The transaction could not be executed due to overflow risk for the pool.
OverflowRisk,
/// A pool must be in [`PoolState::Destroying`] in order for the depositor to unbond or for
/// other members to be permissionlessly unbonded.
NotDestroying,
/// The depositor must be the only member in the bonded pool in order to unbond. And the
/// depositor must be the only member in the sub pools in order to withdraw unbonded.
NotOnlyPoolMember,
/// The caller does not have nominating permissions for the pool.
NotNominator,
/// Either a) the caller cannot make a valid kick or b) the pool is not destroying.
Expand All @@ -1447,8 +1443,6 @@ pub mod pallet {
/// Some error occurred that should never happen. This should be reported to the
/// maintainers.
Defensive(DefensiveError),
/// Not enough points. Ty unbonding less.
NotEnoughPointsToUnbond,
/// Partial unbonding now allowed permissionlessly.
PartialUnbondNotAllowedPermissionlessly,
}
Expand Down Expand Up @@ -1758,12 +1752,7 @@ pub mod pallet {
let mut sub_pools = SubPoolsStorage::<T>::get(member.pool_id)
.defensive_ok_or::<Error<T>>(DefensiveError::SubPoolsNotFound.into())?;

bonded_pool.ok_to_withdraw_unbonded_with(
&caller,
&member_account,
&member,
&sub_pools,
)?;
bonded_pool.ok_to_withdraw_unbonded_with(&caller, &member_account)?;

// NOTE: must do this after we have done the `ok_to_withdraw_unbonded_other_with` check.
let withdrawn_points = member.withdraw_unlocked(current_era);
Expand Down Expand Up @@ -1878,13 +1867,7 @@ pub mod pallet {
) -> DispatchResult {
let who = ensure_signed(origin)?;

ensure!(
amount >=
T::StakingInterface::minimum_bond()
.max(MinCreateBond::<T>::get())
.max(MinJoinBond::<T>::get()),
Error::<T>::MinimumBondNotMet
);
ensure!(amount >= Pallet::<T>::depositor_min_bond(), Error::<T>::MinimumBondNotMet);
ensure!(
MaxPools::<T>::get()
.map_or(true, |max_pools| BondedPools::<T>::count() < max_pools),
Expand Down Expand Up @@ -2162,6 +2145,18 @@ pub mod pallet {
}

impl<T: Config> Pallet<T> {
/// The amount of bond that MUST REMAIN IN BONDED in ALL POOLS.
///
/// It is the responsibility of the depositor to put these funds into the pool initially. Upon
/// unbond, they can never unbond to a value below this amount.
///
/// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former
/// is coming from the staking pallet and the latter two are configured in this pallet.
fn depositor_min_bond() -> BalanceOf<T> {
T::StakingInterface::minimum_bond()
.max(MinCreateBond::<T>::get())
.max(MinJoinBond::<T>::get())
}
/// Remove everything related to the given bonded pool.
///
/// All sub-pools are also deleted. All accounts are dusted and the leftover of the reward
Expand Down
16 changes: 12 additions & 4 deletions frame/nomination-pools/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn default_reward_account() -> AccountId {
}

parameter_types! {
pub static MinJoinBondConfig: Balance = 2;
pub static CurrentEra: EraIndex = 0;
pub static BondingDuration: EraIndex = 3;
pub storage BondedBalanceMap: BTreeMap<AccountId, Balance> = Default::default();
Expand Down Expand Up @@ -245,6 +246,11 @@ impl ExtBuilder {
self
}

pub(crate) fn min_join_bond(self, min: Balance) -> Self {
MinJoinBondConfig::set(min);
self
}

pub(crate) fn with_check(self, level: u8) -> Self {
CheckLevel::set(level);
self
Expand All @@ -261,11 +267,12 @@ impl ExtBuilder {
}

pub(crate) fn build(self) -> sp_io::TestExternalities {
sp_tracing::try_init_simple();
let mut storage =
frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();

let _ = crate::GenesisConfig::<Runtime> {
min_join_bond: 2,
min_join_bond: MinJoinBondConfig::get(),
min_create_bond: 2,
max_pools: Some(2),
max_members_per_pool: self.max_members_per_pool,
Expand All @@ -280,8 +287,8 @@ impl ExtBuilder {
frame_system::Pallet::<Runtime>::set_block_number(1);

// make a pool
let amount_to_bond = <Runtime as pools::Config>::StakingInterface::minimum_bond();
Balances::make_free_balance_be(&10, amount_to_bond * 2);
let amount_to_bond = Pools::depositor_min_bond();
Balances::make_free_balance_be(&10, amount_to_bond * 5);
assert_ok!(Pools::create(RawOrigin::Signed(10).into(), amount_to_bond, 900, 901, 902));

let last_pool = LastPoolId::<Runtime>::get();
Expand All @@ -302,12 +309,13 @@ impl ExtBuilder {
}
}

pub(crate) fn unsafe_set_state(pool_id: PoolId, state: PoolState) -> Result<(), ()> {
pub(crate) fn unsafe_set_state(pool_id: PoolId, state: PoolState) {
BondedPools::<Runtime>::try_mutate(pool_id, |maybe_bonded_pool| {
maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
bonded_pool.state = state;
})
})
.unwrap()
}

parameter_types! {
Expand Down
Loading