Skip to content

Commit

Permalink
Limit process redeem requests (#2806)
Browse files Browse the repository at this point in the history
* limit process redeem requests

* fix benchmarks

* update
  • Loading branch information
wangjj9219 authored Sep 11, 2024
1 parent 536bed6 commit 1c6302f
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 29 deletions.
35 changes: 23 additions & 12 deletions modules/homa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ pub mod module {
/// The XcmInterface to manage the staking of sub-account on relaychain.
type XcmInterface: HomaSubAccountXcm<Self::AccountId, Balance>;

/// The limit for process redeem requests when bump era.
#[pallet::constant]
type ProcessRedeemRequestsLimit: Get<u32>;

/// Weight information for the extrinsics in this module.
type WeightInfo: WeightInfo;

Expand Down Expand Up @@ -383,14 +387,14 @@ pub mod module {
fn on_initialize(_: BlockNumberFor<T>) -> Weight {
let bump_era_number = Self::era_amount_should_to_bump(T::RelayChainBlockNumber::current_block_number());
if !bump_era_number.is_zero() {
let _ = Self::bump_current_era(bump_era_number);
let res = Self::bump_current_era(bump_era_number);
debug_assert_eq!(
TotalStakingBonded::<T>::get(),
StakingLedgers::<T>::iter().fold(Zero::zero(), |total_bonded: Balance, (_, ledger)| {
total_bonded.saturating_add(ledger.bonded)
})
);
<T as Config>::WeightInfo::on_initialize_with_bump_era()
<T as Config>::WeightInfo::on_initialize_with_bump_era(res.unwrap_or_default())
} else {
<T as Config>::WeightInfo::on_initialize()
}
Expand Down Expand Up @@ -659,10 +663,12 @@ pub mod module {
}

#[pallet::call_index(8)]
#[pallet::weight(< T as Config >::WeightInfo::on_initialize_with_bump_era())]
pub fn force_bump_current_era(origin: OriginFor<T>, bump_amount: EraIndex) -> DispatchResult {
#[pallet::weight(< T as Config >::WeightInfo::on_initialize_with_bump_era(T::ProcessRedeemRequestsLimit::get()))]
pub fn force_bump_current_era(origin: OriginFor<T>, bump_amount: EraIndex) -> DispatchResultWithPostInfo {
T::GovernanceOrigin::ensure_origin(origin)?;
Self::bump_current_era(bump_amount)

let res = Self::bump_current_era(bump_amount);
Ok(Some(T::WeightInfo::on_initialize_with_bump_era(res.unwrap_or_default())).into())
}

/// Execute fast match for specific redeem requests, require completely matched.
Expand Down Expand Up @@ -1067,17 +1073,18 @@ pub mod module {

/// Process redeem requests and subaccounts do unbond on relaychain by XCM message.
#[transactional]
pub fn process_redeem_requests(new_era: EraIndex) -> DispatchResult {
pub fn process_redeem_requests(new_era: EraIndex) -> Result<u32, DispatchError> {
let era_index_to_expire = new_era + T::BondingDuration::get();
let total_bonded = TotalStakingBonded::<T>::get();
let mut total_redeem_amount: Balance = Zero::zero();
let mut remain_total_bonded = total_bonded;
let mut handled_requests: u32 = 0;

// iter RedeemRequests and insert to Unbondings if remain_total_bonded is enough.
for (redeemer, (redeem_amount, _)) in RedeemRequests::<T>::iter() {
let redemption_amount = Self::convert_liquid_to_staking(redeem_amount)?;

if remain_total_bonded >= redemption_amount {
if remain_total_bonded >= redemption_amount && handled_requests < T::ProcessRedeemRequestsLimit::get() {
total_redeem_amount = total_redeem_amount.saturating_add(redeem_amount);
remain_total_bonded = remain_total_bonded.saturating_sub(redemption_amount);
RedeemRequests::<T>::remove(&redeemer);
Expand All @@ -1090,6 +1097,8 @@ pub mod module {
liquid_amount: redeem_amount,
unbonding_staking_amount: redemption_amount,
});

handled_requests += 1;
} else {
break;
}
Expand Down Expand Up @@ -1126,7 +1135,9 @@ pub mod module {
}

// burn total_redeem_amount.
Self::burn_liquid_currency(&Self::account_id(), total_redeem_amount)
Self::burn_liquid_currency(&Self::account_id(), total_redeem_amount)?;

Ok(handled_requests)
}

/// Process nominate validators for subaccounts on relaychain.
Expand Down Expand Up @@ -1163,22 +1174,22 @@ pub mod module {
/// The rebalance will send XCM messages to relaychain. Once the XCM message is sent,
/// the execution result cannot be obtained and cannot be rolled back. So the process
/// of rebalance is not atomic.
pub fn bump_current_era(amount: EraIndex) -> DispatchResult {
pub fn bump_current_era(amount: EraIndex) -> Result<u32, DispatchError> {
let previous_era = Self::relay_chain_current_era();
let new_era = previous_era.saturating_add(amount);
RelayChainCurrentEra::<T>::put(new_era);
LastEraBumpedBlock::<T>::put(T::RelayChainBlockNumber::current_block_number());
Self::deposit_event(Event::<T>::CurrentEraBumped { new_era_index: new_era });

// Rebalance:
let res = || -> DispatchResult {
let res = || -> Result<u32, DispatchError> {
TotalVoidLiquid::<T>::put(0);
Self::process_staking_rewards(new_era, previous_era)?;
Self::process_scheduled_unbond(new_era)?;
Self::process_to_bond_pool()?;
Self::process_redeem_requests(new_era)?;
let count = Self::process_redeem_requests(new_era)?;
Self::process_nominate(new_era)?;
Ok(())
Ok(count)
}();

log::debug!(
Expand Down
3 changes: 2 additions & 1 deletion modules/homa/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use super::*;
use frame_support::{
derive_impl, ord_parameter_types, parameter_types,
traits::{ConstU128, Nothing},
traits::{ConstU128, ConstU32, Nothing},
};
use frame_system::{EnsureRoot, EnsureSignedBy};
use module_support::mocks::MockAddressMapping;
Expand Down Expand Up @@ -216,6 +216,7 @@ impl Config for Runtime {
type XcmInterface = MockHomaSubAccountXcm;
type WeightInfo = ();
type NominationsProvider = MockNominationsProvider;
type ProcessRedeemRequestsLimit = ConstU32<3>;
}

type Block = frame_system::mocking::MockBlock<Runtime>;
Expand Down
79 changes: 73 additions & 6 deletions modules/homa/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ fn process_redeem_requests_works() {
);

// total_bonded is enough to process all redeem requests
assert_ok!(Homa::process_redeem_requests(1));
assert_eq!(Homa::process_redeem_requests(1), Ok(1));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::RedeemedByUnbond {
redeemer: ALICE,
era_index_when_unbond: 1,
Expand Down Expand Up @@ -1106,7 +1106,7 @@ fn process_redeem_requests_works() {
);

// total_bonded is not enough to process all redeem requests
assert_ok!(Homa::process_redeem_requests(2));
assert_eq!(Homa::process_redeem_requests(2), Ok(2));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::RedeemedByUnbond {
redeemer: BOB,
era_index_when_unbond: 2,
Expand Down Expand Up @@ -1276,7 +1276,7 @@ fn bump_current_era_works() {
// bump era to #1,
// will process to_bond_pool.
MockRelayBlockNumberProvider::set(100);
assert_ok!(Homa::bump_current_era(1));
assert_eq!(Homa::bump_current_era(1), Ok(0));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::CurrentEraBumped { new_era_index: 1 }));
assert_eq!(Homa::last_era_bumped_block(), 100);
assert_eq!(Homa::relay_chain_current_era(), 1);
Expand Down Expand Up @@ -1307,7 +1307,7 @@ fn bump_current_era_works() {
// bump era to #2,
// accumulate staking reward and draw commission
MockRelayBlockNumberProvider::set(200);
assert_ok!(Homa::bump_current_era(1));
assert_eq!(Homa::bump_current_era(1), Ok(0));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::CurrentEraBumped { new_era_index: 2 }));
assert_eq!(Homa::last_era_bumped_block(), 200);
assert_eq!(Homa::relay_chain_current_era(), 2);
Expand Down Expand Up @@ -1358,7 +1358,7 @@ fn bump_current_era_works() {
// bump era to #3,
// will process redeem requests
MockRelayBlockNumberProvider::set(300);
assert_ok!(Homa::bump_current_era(1));
assert_eq!(Homa::bump_current_era(1), Ok(1));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::CurrentEraBumped { new_era_index: 3 }));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::RedeemedByUnbond {
redeemer: ALICE,
Expand Down Expand Up @@ -1404,7 +1404,7 @@ fn bump_current_era_works() {
// bump era to #31,
// will process scheduled unbonded
MockRelayBlockNumberProvider::set(3100);
assert_ok!(Homa::bump_current_era(28));
assert_eq!(Homa::bump_current_era(28), Ok(0));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::CurrentEraBumped { new_era_index: 31 }));
assert_eq!(Homa::last_era_bumped_block(), 3100);
assert_eq!(Homa::relay_chain_current_era(), 31);
Expand Down Expand Up @@ -1483,3 +1483,70 @@ fn last_era_bumped_block_config_check_works() {
assert_eq!(MockRelayBlockNumberProvider::current_block_number(), 100);
});
}

#[test]
fn process_redeem_requests_under_limit_works() {
ExtBuilder::default()
.balances(vec![
(ALICE, LIQUID_CURRENCY_ID, 10_000_000),
(BOB, LIQUID_CURRENCY_ID, 10_000_000),
(CHARLIE, LIQUID_CURRENCY_ID, 10_000_000),
(DAVE, LIQUID_CURRENCY_ID, 10_000_000),
])
.build()
.execute_with(|| {
assert_ok!(Homa::reset_ledgers(
RuntimeOrigin::signed(HomaAdmin::get()),
vec![(0, Some(4_000_000), None)]
));
ToBondPool::<Runtime>::put(4_000_000);

assert_ok!(Homa::request_redeem(RuntimeOrigin::signed(ALICE), 5_000_000, false));
assert_ok!(Homa::request_redeem(RuntimeOrigin::signed(BOB), 5_000_000, false));
assert_ok!(Homa::request_redeem(RuntimeOrigin::signed(CHARLIE), 5_000_000, false));
assert_ok!(Homa::request_redeem(RuntimeOrigin::signed(DAVE), 5_000_000, false));
assert_eq!(Homa::redeem_requests(&ALICE), Some((5_000_000, false)));
assert_eq!(Homa::redeem_requests(&BOB), Some((5_000_000, false)));
assert_eq!(Homa::redeem_requests(&CHARLIE), Some((5_000_000, false)));
assert_eq!(Homa::redeem_requests(&DAVE), Some((5_000_000, false)));
assert_eq!(Homa::unbondings(&ALICE, 1 + BondingDuration::get()), 0);
assert_eq!(Homa::unbondings(&BOB, 1 + BondingDuration::get()), 0);
assert_eq!(Homa::unbondings(&CHARLIE, 1 + BondingDuration::get()), 0);
assert_eq!(Homa::unbondings(&DAVE, 1 + BondingDuration::get()), 0);
assert_eq!(Homa::get_total_bonded(), 4_000_000);
assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 40_000_000);

// total_bonded is enough to process all redeem requests, but excceed limit
assert_eq!(Homa::process_redeem_requests(1), Ok(3));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::RedeemedByUnbond {
redeemer: ALICE,
era_index_when_unbond: 1,
liquid_amount: 5_000_000,
unbonding_staking_amount: 1_000_000,
}));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::RedeemedByUnbond {
redeemer: BOB,
era_index_when_unbond: 1,
liquid_amount: 5_000_000,
unbonding_staking_amount: 1_000_000,
}));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::RedeemedByUnbond {
redeemer: CHARLIE,
era_index_when_unbond: 1,
liquid_amount: 5_000_000,
unbonding_staking_amount: 1_000_000,
}));
System::assert_has_event(RuntimeEvent::Homa(crate::Event::HomaUnbond {
sub_account_index: 0,
amount: 3_000_000,
}));
assert_eq!(Homa::redeem_requests(&ALICE), None);
assert_eq!(Homa::redeem_requests(&BOB), None);
assert_eq!(Homa::redeem_requests(&CHARLIE), None);
assert_eq!(Homa::redeem_requests(&DAVE), Some((5_000_000, false)));
assert_eq!(Homa::unbondings(&ALICE, 1 + BondingDuration::get()), 1_000_000);
assert_eq!(Homa::unbondings(&BOB, 1 + BondingDuration::get()), 1_000_000);
assert_eq!(Homa::unbondings(&CHARLIE, 1 + BondingDuration::get()), 1_000_000);
assert_eq!(Homa::unbondings(&DAVE, 1 + BondingDuration::get()), 0);
});
}
10 changes: 7 additions & 3 deletions modules/homa/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use sp_std::marker::PhantomData;
/// Weight functions needed for module_homa.
pub trait WeightInfo {
fn on_initialize() -> Weight;
fn on_initialize_with_bump_era() -> Weight;
fn on_initialize_with_bump_era(n: u32,) -> Weight;
fn mint() -> Weight;
fn request_redeem() -> Weight;
fn fast_match_redeems(n: u32, ) -> Weight;
Expand Down Expand Up @@ -92,10 +92,12 @@ impl<T: frame_system::Config> WeightInfo for AcalaWeight<T> {
// Storage: Homa RedeemRequests (r:2 w:1)
// Storage: Homa Unbondings (r:1 w:1)
// Storage: Homa TotalVoidLiquid (r:0 w:1)
fn on_initialize_with_bump_era() -> Weight {
fn on_initialize_with_bump_era(n: u32,) -> Weight {
Weight::from_parts(253_506_000, 0)
.saturating_add(T::DbWeight::get().reads(31 as u64))
.saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(n as u64)))
.saturating_add(T::DbWeight::get().writes(18 as u64))
.saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(n as u64)))
}
// Storage: unknown [0x3a7472616e73616374696f6e5f6c6576656c3a] (r:1 w:1)
// Storage: Homa TotalStakingBonded (r:1 w:0)
Expand Down Expand Up @@ -195,10 +197,12 @@ impl WeightInfo for () {
Weight::from_parts(5_281_000, 0)
.saturating_add(RocksDbWeight::get().reads(3 as u64))
}
fn on_initialize_with_bump_era() -> Weight {
fn on_initialize_with_bump_era(n: u32,) -> Weight {
Weight::from_parts(253_506_000, 0)
.saturating_add(RocksDbWeight::get().reads(31 as u64))
.saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(n as u64)))
.saturating_add(RocksDbWeight::get().writes(18 as u64))
.saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(n as u64)))
}
fn mint() -> Weight {
Weight::from_parts(88_950_000, 0)
Expand Down
1 change: 1 addition & 0 deletions runtime/acala/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1594,6 +1594,7 @@ impl module_homa::Config for Runtime {
type XcmInterface = XcmInterface;
type WeightInfo = weights::module_homa::WeightInfo<Runtime>;
type NominationsProvider = NomineesElection;
type ProcessRedeemRequestsLimit = ConstU32<2_000>;
}

parameter_types! {
Expand Down
4 changes: 3 additions & 1 deletion runtime/acala/src/weights/module_homa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,16 @@ impl<T: frame_system::Config> module_homa::WeightInfo for WeightInfo<T> {
// Proof: `Homa::Unbondings` (`max_values`: None, `max_size`: None, mode: `Measured`)
// Storage: `Homa::TotalVoidLiquid` (r:0 w:1)
// Proof: `Homa::TotalVoidLiquid` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn on_initialize_with_bump_era() -> Weight {
fn on_initialize_with_bump_era(n: u32,) -> Weight {
// Proof Size summary in bytes:
// Measured: `2961`
// Estimated: `13851`
// Minimum execution time: 298_418 nanoseconds.
Weight::from_parts(305_164_000, 13851)
.saturating_add(T::DbWeight::get().reads(32))
.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(n.into())))
.saturating_add(T::DbWeight::get().writes(18))
.saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into())))
}
// Storage: `Homa::TotalStakingBonded` (r:1 w:0)
// Proof: `Homa::TotalStakingBonded` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
Expand Down
1 change: 1 addition & 0 deletions runtime/common/src/precompile/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ impl module_homa::Config for Test {
type XcmInterface = MockHomaSubAccountXcm;
type WeightInfo = ();
type NominationsProvider = ();
type ProcessRedeemRequestsLimit = ConstU32<2_000>;
}

parameter_type_with_key! {
Expand Down
1 change: 1 addition & 0 deletions runtime/karura/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,7 @@ impl module_homa::Config for Runtime {
type XcmInterface = XcmInterface;
type WeightInfo = weights::module_homa::WeightInfo<Runtime>;
type NominationsProvider = NomineesElection;
type ProcessRedeemRequestsLimit = ConstU32<2_000>;
}

parameter_types! {
Expand Down
4 changes: 3 additions & 1 deletion runtime/karura/src/weights/module_homa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,16 @@ impl<T: frame_system::Config> module_homa::WeightInfo for WeightInfo<T> {
// Proof: `Homa::Unbondings` (`max_values`: None, `max_size`: None, mode: `Measured`)
// Storage: `Homa::TotalVoidLiquid` (r:0 w:1)
// Proof: `Homa::TotalVoidLiquid` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn on_initialize_with_bump_era() -> Weight {
fn on_initialize_with_bump_era(n: u32,) -> Weight {
// Proof Size summary in bytes:
// Measured: `2962`
// Estimated: `13852`
// Minimum execution time: 314_492 nanoseconds.
Weight::from_parts(320_994_000, 13852)
.saturating_add(T::DbWeight::get().reads(34))
.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(n.into())))
.saturating_add(T::DbWeight::get().writes(19))
.saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into())))
}
// Storage: `Homa::TotalStakingBonded` (r:1 w:0)
// Proof: `Homa::TotalStakingBonded` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
Expand Down
Loading

0 comments on commit 1c6302f

Please sign in to comment.