Skip to content

Commit

Permalink
Merge pull request #2592 from subspace/submit-fp-benchmark
Browse files Browse the repository at this point in the history
Update pallet-domains benchmarking
  • Loading branch information
NingLin-P authored Mar 15, 2024
2 parents 3b9c276 + 020c104 commit 0905b9d
Show file tree
Hide file tree
Showing 14 changed files with 1,670 additions and 664 deletions.
1 change: 1 addition & 0 deletions crates/pallet-domains/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,6 @@ runtime-benchmarks = [
"frame-benchmarking",
"frame-benchmarking/runtime-benchmarks",
"sp-domains/runtime-benchmarks",
"sp-domains-fraud-proof/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
4 changes: 3 additions & 1 deletion crates/pallet-domains/res/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ This runtime is used for benchmarking the `register_domain_runtime` and `upgrade

**Don't use them in production environments!**

To update the just copy the new runtime from `target/release/wbuild/evm-domain-test-runtime/evm_domain_test_runtime.compact.compressed.wasm` to here.
To update:
1. Build the `subspace-node` binary
2. Run `subspace-node domain build-genesis-storage --chain dev > evm-domain-genesis-storage`
Binary file not shown.
671 changes: 574 additions & 97 deletions crates/pallet-domains/src/benchmarking.rs

Large diffs are not rendered by default.

189 changes: 144 additions & 45 deletions crates/pallet-domains/src/lib.rs

Large diffs are not rendered by default.

21 changes: 17 additions & 4 deletions crates/pallet-domains/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,11 @@ pub(crate) fn do_nominate_operator<T: Config>(
Error::OperatorNotRegistered
);

// If the this is the first staking request of this operator `note_pending_staking_operation` for it
if operator.deposits_in_epoch.is_zero() && operator.withdrawals_in_epoch.is_zero() {
note_pending_staking_operation::<T>(operator.current_domain_id)?;
}

let domain_stake_summary = DomainStakingSummary::<T>::get(operator.current_domain_id)
.ok_or(Error::DomainNotInitialized)?;

Expand Down Expand Up @@ -727,8 +732,6 @@ pub(crate) fn do_deregister_operator<T: Config>(
Operators::<T>::try_mutate(operator_id, |maybe_operator| {
let operator = maybe_operator.as_mut().ok_or(Error::UnknownOperator)?;

note_pending_staking_operation::<T>(operator.current_domain_id)?;

ensure!(
*operator.status::<T>(operator_id) == OperatorStatus::Registered,
Error::OperatorNotRegistered
Expand Down Expand Up @@ -776,6 +779,11 @@ pub(crate) fn do_withdraw_stake<T: Config>(

ensure!(!shares_withdrew.is_zero(), Error::ZeroWithdrawShares);

// If the this is the first staking request of this operator `note_pending_staking_operation` for it
if operator.deposits_in_epoch.is_zero() && operator.withdrawals_in_epoch.is_zero() {
note_pending_staking_operation::<T>(operator.current_domain_id)?;
}

// calculate shares for any previous epoch
let domain_stake_summary = DomainStakingSummary::<T>::get(operator.current_domain_id)
.ok_or(Error::DomainNotInitialized)?;
Expand Down Expand Up @@ -1046,7 +1054,9 @@ pub(crate) fn do_unlock_funds<T: Config>(
}

/// Unlocks an already de-registered operator given unlock wait period is complete.
pub(crate) fn do_unlock_operator<T: Config>(operator_id: OperatorId) -> Result<(), Error> {
///
/// Return the number of nominator processed
pub(crate) fn do_unlock_operator<T: Config>(operator_id: OperatorId) -> Result<usize, Error> {
Operators::<T>::try_mutate_exists(operator_id, |maybe_operator| {
// take the operator so this operator info is removed once we unlock the operator.
let operator = maybe_operator.take().ok_or(Error::UnknownOperator)?;
Expand Down Expand Up @@ -1081,6 +1091,7 @@ pub(crate) fn do_unlock_operator<T: Config>(operator_id: OperatorId) -> Result<(
operator.total_storage_fee_deposit,
);
let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(operator_id);
let mut nominator_count = 0;
Deposits::<T>::drain_prefix(operator_id).try_for_each(|(nominator_id, mut deposit)| {
// convert any deposits from the previous epoch to shares
do_convert_previous_epoch_deposits::<T>(operator_id, &mut deposit)?;
Expand Down Expand Up @@ -1160,6 +1171,8 @@ pub(crate) fn do_unlock_operator<T: Config>(operator_id: OperatorId) -> Result<(
T::Currency::release_all(&storage_fund_hold_id, &nominator_id, Precision::Exact)
.map_err(|_| Error::RemoveLock)?;

nominator_count += 1;

Ok(())
})?;

Expand All @@ -1182,7 +1195,7 @@ pub(crate) fn do_unlock_operator<T: Config>(operator_id: OperatorId) -> Result<(
// remove nominator count for this operator.
NominatorCount::<T>::remove(operator_id);

Ok(())
Ok(nominator_count)
})
}

Expand Down
194 changes: 114 additions & 80 deletions crates/pallet-domains/src/staking_epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,48 @@ pub enum Error {
SlashOperator(TransitionError),
}

pub(crate) struct EpochTransitionResult {
pub rewarded_operator_count: u32,
pub slashed_nominator_count: u32,
pub finalized_operator_count: u32,
pub completed_epoch_index: EpochIndex,
}

/// Finalizes the domain's current epoch and begins the next epoch.
/// Returns true of the epoch indeed was finished.
/// Returns true of the epoch indeed was finished and the number of operator processed.
pub(crate) fn do_finalize_domain_current_epoch<T: Config>(
domain_id: DomainId,
) -> Result<EpochIndex, Error> {
) -> Result<EpochTransitionResult, Error> {
// Reset pending staking operation count to 0
PendingStakingOperationCount::<T>::set(domain_id, 0);

// re stake operator's tax from the rewards
operator_take_reward_tax_and_stake::<T>(domain_id)?;
let rewarded_operator_count = operator_take_reward_tax_and_stake::<T>(domain_id)?;

// slash the operators
do_finalize_slashed_operators::<T>(domain_id).map_err(Error::SlashOperator)?;
let slashed_nominator_count =
do_finalize_slashed_operators::<T>(domain_id).map_err(Error::SlashOperator)?;

// finalize any operator switches
do_finalize_switch_operator_domain::<T>(domain_id)?;

// finalize any withdrawals and then deposits
do_finalize_domain_epoch_staking::<T>(domain_id)
let (completed_epoch_index, finalized_operator_count) =
do_finalize_domain_epoch_staking::<T>(domain_id)?;

Ok(EpochTransitionResult {
rewarded_operator_count,
slashed_nominator_count,
finalized_operator_count,
completed_epoch_index,
})
}

/// Operator takes `NominationTax` of the current epoch rewards and stake them.
pub(crate) fn operator_take_reward_tax_and_stake<T: Config>(
domain_id: DomainId,
) -> Result<(), Error> {
) -> Result<u32, Error> {
let mut rewarded_operator_count = 0;
DomainStakingSummary::<T>::try_mutate(domain_id, |maybe_domain_stake_summary| {
let stake_summary = maybe_domain_stake_summary
.as_mut()
Expand Down Expand Up @@ -126,13 +143,17 @@ pub(crate) fn operator_take_reward_tax_and_stake<T: Config>(
.checked_add(&rewards)
.ok_or(TransitionError::BalanceOverflow)?;

rewarded_operator_count += 1;

Ok(())
})?;
}

Ok(())
})
.map_err(Error::OperatorRewardStaking)
.map_err(Error::OperatorRewardStaking)?;

Ok(rewarded_operator_count)
}

/// Add all the switched operators to new domain as next operators.
Expand Down Expand Up @@ -188,7 +209,8 @@ fn switch_operator<T: Config>(

pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
domain_id: DomainId,
) -> Result<EpochIndex, Error> {
) -> Result<(EpochIndex, u32), Error> {
let mut finalized_operator_count = 0;
DomainStakingSummary::<T>::try_mutate(domain_id, |maybe_stake_summary| {
let stake_summary = maybe_stake_summary
.as_mut()
Expand All @@ -210,7 +232,7 @@ pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
continue;
}

let operator_stake = do_finalize_operator_epoch_staking::<T>(
let (operator_stake, stake_changed) = do_finalize_operator_epoch_staking::<T>(
domain_id,
*next_operator_id,
previous_epoch,
Expand All @@ -221,6 +243,10 @@ pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
.ok_or(TransitionError::BalanceOverflow)?;
current_operators.insert(*next_operator_id, operator_stake);
next_operators.insert(*next_operator_id);

if stake_changed {
finalized_operator_count += 1;
}
}

let election_verification_params = ElectionVerificationParams {
Expand All @@ -236,90 +262,95 @@ pub(crate) fn do_finalize_domain_epoch_staking<T: Config>(
stake_summary.current_operators = current_operators;
stake_summary.next_operators = next_operators;

Ok(previous_epoch)
Ok((previous_epoch, finalized_operator_count))
})
.map_err(Error::FinalizeDomainEpochStaking)
}

/// Finalize the epoch for the operator
///
/// Return the new total stake of the operator and a bool indicate if its total stake
/// is changed due to deposit/withdraw/reward happened in the previous epoch
pub(crate) fn do_finalize_operator_epoch_staking<T: Config>(
domain_id: DomainId,
operator_id: OperatorId,
previous_epoch: EpochIndex,
) -> Result<BalanceOf<T>, TransitionError> {
Operators::<T>::try_mutate(operator_id, |maybe_operator| {
let operator = maybe_operator
.as_mut()
.ok_or(TransitionError::UnknownOperator)?;

if *operator.status::<T>(operator_id) != OperatorStatus::Registered {
return Err(TransitionError::OperatorNotRegistered);
}

// if there are no deposits, withdrawls, and epoch rewards for this operator
// then short-circuit and return early.
if operator.deposits_in_epoch.is_zero()
&& operator.withdrawals_in_epoch.is_zero()
&& operator.current_epoch_rewards.is_zero()
{
return Ok(operator.current_total_stake);
}

let total_stake = operator
.current_total_stake
.checked_add(&operator.current_epoch_rewards)
.ok_or(TransitionError::BalanceOverflow)?;
) -> Result<(BalanceOf<T>, bool), TransitionError> {
let mut operator = match Operators::<T>::get(operator_id) {
Some(op) => op,
None => return Err(TransitionError::UnknownOperator),
};

let total_shares = operator.current_total_shares;
if *operator.status::<T>(operator_id) != OperatorStatus::Registered {
return Err(TransitionError::OperatorNotRegistered);
}

let share_price = SharePrice::new::<T>(total_shares, total_stake);
// if there are no deposits, withdrawls, and epoch rewards for this operator
// then short-circuit and return early.
if operator.deposits_in_epoch.is_zero()
&& operator.withdrawals_in_epoch.is_zero()
&& operator.current_epoch_rewards.is_zero()
{
return Ok((operator.current_total_stake, false));
}

// calculate and subtract total withdrew shares from previous epoch
let (total_stake, total_shares) = if !operator.withdrawals_in_epoch.is_zero() {
let withdraw_stake = share_price.shares_to_stake::<T>(operator.withdrawals_in_epoch);
let total_stake = total_stake
.checked_sub(&withdraw_stake)
.ok_or(TransitionError::BalanceUnderflow)?;
let total_shares = total_shares
.checked_sub(&operator.withdrawals_in_epoch)
.ok_or(TransitionError::ShareUnderflow)?;
let total_stake = operator
.current_total_stake
.checked_add(&operator.current_epoch_rewards)
.ok_or(TransitionError::BalanceOverflow)?;

let total_shares = operator.current_total_shares;

let share_price = SharePrice::new::<T>(total_shares, total_stake);

// calculate and subtract total withdrew shares from previous epoch
let (total_stake, total_shares) = if !operator.withdrawals_in_epoch.is_zero() {
let withdraw_stake = share_price.shares_to_stake::<T>(operator.withdrawals_in_epoch);
let total_stake = total_stake
.checked_sub(&withdraw_stake)
.ok_or(TransitionError::BalanceUnderflow)?;
let total_shares = total_shares
.checked_sub(&operator.withdrawals_in_epoch)
.ok_or(TransitionError::ShareUnderflow)?;

operator.withdrawals_in_epoch = Zero::zero();
(total_stake, total_shares)
} else {
(total_stake, total_shares)
};

operator.withdrawals_in_epoch = Zero::zero();
(total_stake, total_shares)
} else {
(total_stake, total_shares)
};
// calculate and add total deposits from the previous epoch
let (total_stake, total_shares) = if !operator.deposits_in_epoch.is_zero() {
let deposited_shares = share_price.stake_to_shares::<T>(operator.deposits_in_epoch);
let total_stake = total_stake
.checked_add(&operator.deposits_in_epoch)
.ok_or(TransitionError::BalanceOverflow)?;
let total_shares = total_shares
.checked_add(&deposited_shares)
.ok_or(TransitionError::ShareOverflow)?;
operator.deposits_in_epoch = Zero::zero();
(total_stake, total_shares)
} else {
(total_stake, total_shares)
};

// calculate and add total deposits from the previous epoch
let (total_stake, total_shares) = if !operator.deposits_in_epoch.is_zero() {
let deposited_shares = share_price.stake_to_shares::<T>(operator.deposits_in_epoch);
let total_stake = total_stake
.checked_add(&operator.deposits_in_epoch)
.ok_or(TransitionError::BalanceOverflow)?;
let total_shares = total_shares
.checked_add(&deposited_shares)
.ok_or(TransitionError::ShareOverflow)?;
operator.deposits_in_epoch = Zero::zero();
(total_stake, total_shares)
} else {
(total_stake, total_shares)
};
// update operator pool epoch share price
// TODO: once we have reference counting, we do not need to
// store this for every epoch for every operator but instead
// store only those share prices of operators which has either a deposit or withdraw
OperatorEpochSharePrice::<T>::insert(
operator_id,
DomainEpoch::from((domain_id, previous_epoch)),
share_price,
);

// update operator pool epoch share price
// TODO: once we have reference counting, we do not need to
// store this for every epoch for every operator but instead
// store only those share prices of operators which has either a deposit or withdraw
OperatorEpochSharePrice::<T>::insert(
operator_id,
DomainEpoch::from((domain_id, previous_epoch)),
share_price,
);
// update operator state
operator.current_total_shares = total_shares;
operator.current_total_stake = total_stake;
operator.current_epoch_rewards = Zero::zero();
Operators::<T>::set(operator_id, Some(operator));

// update operator state
operator.current_total_shares = total_shares;
operator.current_total_stake = total_stake;
operator.current_epoch_rewards = Zero::zero();
Ok(total_stake)
})
Ok((total_stake, true))
}

pub(crate) fn mint_funds<T: Config>(
Expand All @@ -336,7 +367,8 @@ pub(crate) fn mint_funds<T: Config>(

pub(crate) fn do_finalize_slashed_operators<T: Config>(
domain_id: DomainId,
) -> Result<(), TransitionError> {
) -> Result<u32, TransitionError> {
let mut slashed_nominator_count = 0;
for operator_id in PendingSlashes::<T>::take(domain_id).unwrap_or_default() {
Operators::<T>::try_mutate_exists(operator_id, |maybe_operator| {
// take the operator so this operator info is removed once we slash the operator.
Expand Down Expand Up @@ -468,6 +500,8 @@ pub(crate) fn do_finalize_slashed_operators<T: Config>(
)
.map_err(|_| TransitionError::RemoveLock)?;

slashed_nominator_count += 1;

Ok(())
},
)?;
Expand All @@ -483,7 +517,7 @@ pub(crate) fn do_finalize_slashed_operators<T: Config>(
})?;
}

Ok(())
Ok(slashed_nominator_count)
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ parameter_types! {
pub const StakeEpochDuration: DomainBlockNumber = 5;
pub TreasuryAccount: u128 = PalletId(*b"treasury").into_account_truncating();
pub const BlockReward: Balance = 10 * SSC;
pub const MaxPendingStakingOperation: u32 = 100;
pub const MaxPendingStakingOperation: u32 = 512;
pub const MaxNominators: u32 = 5;
pub const DomainsPalletId: PalletId = PalletId(*b"domains_");
pub const DomainChainByteFee: Balance = 1;
Expand Down
Loading

0 comments on commit 0905b9d

Please sign in to comment.