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

nomination-pools: allow pool-ids to be reused #12407

Merged
merged 13 commits into from
Oct 29, 2022
99 changes: 93 additions & 6 deletions frame/nomination-pools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,10 @@ pub mod pallet {
Defensive(DefensiveError),
/// Partial unbonding now allowed permissionlessly.
PartialUnbondNotAllowedPermissionlessly,
/// Pool id currently in use.
PoolIdInUse,
/// Claim exceeds the last pool id.
PoolIdCountExceeded
}

#[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)]
Expand Down Expand Up @@ -1943,25 +1947,108 @@ pub mod pallet {
Ok(())
}

/*

// TODO: Benchmark?
/// Doesn't mean that the poolId has been removed from the BondedPools, just means that there is no active depositor and no stake in the pool
/// Create a new delegation pool with a previously used pool id
///
/// # Arguments
///
/// same as `create` with the inclusion of
/// * `pool_id` - `Option<PoolId>`, if `None` this is similar to `create`.
/// if `Some(claim)` the caller is claiming that `claim` A.K.A PoolId is not in use.
pub fn create_with_pool_id() {
#[pallet::weight(T::WeightInfo::create())]
#[transactional]
pub fn create_with_pool_id(
origin: OriginFor<T>,
#[pallet::compact] amount: BalanceOf<T>,
root: AccountIdLookupOf<T>,
nominator: AccountIdLookupOf<T>,
state_toggler: AccountIdLookupOf<T>,
pool_id: Option<PoolId>,
} -> DispatchResult {

state_claim: Option<PoolId>,
Doordashcon marked this conversation as resolved.
Show resolved Hide resolved
) -> DispatchResult {
let who = ensure_signed(origin)?;
let root = T::Lookup::lookup(root)?;
let nominator = T::Lookup::lookup(nominator)?;
Doordashcon marked this conversation as resolved.
Show resolved Hide resolved
let state_toggler = T::Lookup::lookup(state_toggler)?;

ensure!(amount >= Pallet::<T>::depositor_min_bond(), Error::<T>::MinimumBondNotMet);
ensure!(
MaxPools::<T>::get()
.map_or(true, |max_pools| BondedPools::<T>::count() < max_pools),
Error::<T>::MaxPools
);
ensure!(!PoolMembers::<T>::contains_key(&who), Error::<T>::AccountBelongsToOtherPool);

let pool_id = match state_claim {
Some(claim) => {
ensure!(!BondedPools::<T>::contains_key(claim), Error::<T>::PoolIdInUse); // create custom Error?
ensure!(claim < LastPoolId::<T>::get(), Error::<T>::PoolIdCountExceeded);
claim
},
None => {
let inc_pool_id = LastPoolId::<T>::try_mutate::<_, Error<T>, _>(|id| {
*id = id.checked_add(1).ok_or(Error::<T>::OverflowRisk)?;
Ok(*id)
})?;
inc_pool_id
},
};

let mut bonded_pool = BondedPool::<T>::new(
pool_id,
PoolRoles {
root: Some(root),
nominator: Some(nominator),
state_toggler: Some(state_toggler),
depositor: who.clone(),
},
);

bonded_pool.try_inc_members()?;
let points = bonded_pool.try_bond_funds(&who, amount, BondType::Create)?;

T::Currency::transfer(
&who,
&bonded_pool.reward_account(),
T::Currency::minimum_balance(),
ExistenceRequirement::AllowDeath,
)?;

PoolMembers::<T>::insert(
who.clone(),
PoolMember::<T> {
pool_id,
points,
last_recorded_reward_counter: Zero::zero(),
unbonding_eras: Default::default(),
},
);
RewardPools::<T>::insert(
pool_id,
RewardPool::<T> {
last_recorded_reward_counter: Zero::zero(),
last_recorded_total_payouts: Zero::zero(),
total_rewards_claimed: Zero::zero(),
},
);
ReversePoolIdLookup::<T>::insert(bonded_pool.bonded_account(), pool_id);

Self::deposit_event(Event::<T>::Created { depositor: who.clone(), pool_id });

Self::deposit_event(Event::<T>::Bonded {
member: who,
pool_id,
bonded: amount,
joined: true,
});
bonded_pool.put();

Ok(())
// ensure bondedpoools contains that pool_id, this needs a custom error. // THIS should be inside the match statement

// let result = match Option ( if Some(claim), if None then increment the last pool Id)
}
*/

/// Nominate on behalf of the pool.
///
Expand Down
2 changes: 1 addition & 1 deletion frame/nomination-pools/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ impl ExtBuilder {
let _ = crate::GenesisConfig::<Runtime> {
min_join_bond: MinJoinBondConfig::get(),
min_create_bond: 2,
max_pools: Some(2),
max_pools: Some(3),
max_members_per_pool: self.max_members_per_pool,
max_members: self.max_members,
}
Expand Down
61 changes: 59 additions & 2 deletions frame/nomination-pools/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4170,9 +4170,21 @@ mod create {
},
}
.put();
assert_eq!(MaxPools::<Runtime>::get(), Some(2));
assert_eq!(MaxPools::<Runtime>::get(), Some(3));
assert_eq!(BondedPools::<Runtime>::count(), 2);

BondedPool::<Runtime> {
id: 3,
inner: BondedPoolInner {
state: PoolState::Open,
points: 10,
member_counter: 1,
roles: DEFAULT_ROLES,
},
}
.put();
assert_eq!(BondedPools::<Runtime>::count(), 3);

// Then
assert_noop!(
Pools::create(RuntimeOrigin::signed(11), 20, 123, 456, 789),
Expand All @@ -4181,7 +4193,7 @@ mod create {

// Given
assert_eq!(PoolMembers::<Runtime>::count(), 1);
MaxPools::<Runtime>::put(3);
MaxPools::<Runtime>::put(4);
MaxPoolMembers::<Runtime>::put(1);
Balances::make_free_balance_be(&11, 5 + 20);

Expand All @@ -4198,6 +4210,51 @@ mod create {
);
});
}

#[test]
fn create_with_pool_id_works() {
ExtBuilder::default().build_and_execute(|| {
let ed = Balances::minimum_balance();

Balances::make_free_balance_be(&11, StakingMock::minimum_bond() + ed);
assert_ok!(Pools::create(
RuntimeOrigin::signed(11),
StakingMock::minimum_bond(),
123,
456,
789
));
Comment on lines +4207 to +4214
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are accounts created in mock.rs that are already funded. You need not create a new one.

also, I think this test will pass entirely without you creating this new pool as well, not sure what you are testing here.

Copy link
Contributor Author

@Doordashcon Doordashcon Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an assumption I have that I would like clarified....If there is only ever one nomination pool created the LastPoolId stays at 1, there is no logic to decrement the LastPoolId when it is destroyed.


assert_eq!(Balances::free_balance(&11), 0);
// delete the initial pool created, then pool_Id `1` will be free

assert_noop!(Pools::create_with_pool_id(
RuntimeOrigin::signed(12),
20,
234,
654,
783,
Some(1)
), Error::<Runtime>::PoolIdInUse);

// start dismantling the pool.
assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying));
assert_ok!(fully_unbond_permissioned(10));

CurrentEra::set(3);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 10));

assert_ok!(Pools::create_with_pool_id(
RuntimeOrigin::signed(10),
20,
234,
654,
783,
Some(1)
));
});

}
}

mod nominate {
Expand Down