Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions pallets/parachain-staking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
]
std = [
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
Expand Down
95 changes: 81 additions & 14 deletions pallets/parachain-staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin};
use pallet_session::Pallet as Session;
use sp_runtime::{
traits::{One, SaturatedConversion, StaticLookup},
Permill,
Permill, Saturating,
};
use sp_std::{convert::TryInto, vec::Vec};

Expand Down Expand Up @@ -139,13 +139,11 @@ benchmarks! {
assert_eq!(<Round<T>>::get().current, 0u32);
}

on_initialize_round_update {
let round = <Round<T>>::get();
assert_eq!(round.current, 0u32);
}: { Pallet::<T>::on_initialize(round.length) }
verify {
assert_eq!(<Round<T>>::get().current, 1u32);
}
// NOTE: the `on_initialize_round_update` benchmark was removed -- round rotation moved
// from this pallet's on_initialize to pallet_session (start_session), so on_initialize no
// longer advances the round and the old benchmark asserted behaviour that cannot happen.
// The WeightInfo::on_initialize_round_update method is left in place (now unused) rather
// than removed here, as that touches the trait contract beyond this change.

force_new_round {
let round = <Round<T>>::get();
Expand Down Expand Up @@ -287,12 +285,12 @@ benchmarks! {
// go to block in which we can exit
assert_ok!(<Pallet<T>>::init_leave_candidates(RawOrigin::Signed(candidate.clone()).into()));

for i in 1..=T::ExitQueueDelay::get() {
let round = <Round<T>>::get();
let now = round.first + round.length;
System::<T>::set_block_number(now);
Pallet::<T>::on_initialize(now);
}
// Round rotation now lives in pallet_session, so this pallet's on_initialize no longer
// advances the round. Bump the round counter directly by ExitQueueDelay so the exit
// delay elapses and the candidate can execute its leave (can_exit checks Round.current).
<Round<T>>::mutate(|round| {
round.current = round.current.saturating_add(T::ExitQueueDelay::get());
});
let unlookup_candidate = T::Lookup::unlookup(candidate.clone());

}: _(RawOrigin::Signed(candidate.clone()), unlookup_candidate)
Expand Down Expand Up @@ -610,6 +608,75 @@ benchmarks! {
// let state = <CandidatePool<T>>::get(&collator).unwrap();
// assert!(state.delegators.into_iter().any(|x| x.owner == delegator);
// }

// Session-boundary (snapshot) cost: at each rotation, prepare_delayed_rewards snapshots ALL
// selected collators (n), each carrying its delegators (m), into AtStake in ONE block -- the
// heaviest single block in the payout flow (payout_collator, by contrast, is one collator per
// block). Benchmarking this gives the 64x100 worst-case block weight/PoV for block-stuck
// analysis. NOTE: this measures pre-existing session-rotation code, not the restake feature.
prepare_delayed_rewards {
let n in (T::MinCollators::get()) .. T::MaxTopCandidates::get();
let m in 0 .. T::MaxDelegatorsPerCollator::get();

// n collators, each with m delegators, in the live CandidatePool
let candidates = setup_collator_candidates::<T>(n, None);
for (i, c) in candidates.iter().enumerate() {
fill_delegators::<T>(m, c.clone(), i.saturated_into::<u32>());
}

// seed the previous round (old_round = 1): each collator's snapshot + one authored
// block, so get_total_collator_staking_num has data to sum during the call
let old_round: u32 = 1;
for c in candidates.iter() {
let state = CandidatePool::<T>::get(c).unwrap();
AtStake::<T>::insert(old_round, c, state);
CollatorBlocks::<T>::insert(old_round, c, 1u32);
}
let pot = Pallet::<T>::account_id();
T::Currency::make_free_balance_be(&pot, T::MinCollatorCandidateStake::get());
// current round = old_round + 1: snapshots `candidates` into the new round and computes
// old_round's payout totals -- the heaviest per-block work in the whole flow
Round::<T>::mutate(|r| { r.current = old_round + 1; });
}: {
Pallet::<T>::prepare_delayed_rewards(&candidates, old_round + 1);
}
verify {
assert!(DelayedPayoutInfo::<T>::exists());
}

payout_collator {
let n in 0 .. T::MaxDelegatorsPerCollator::get();

// one authoring collator with `n` delegators (present in live state + the snapshot)
let candidates = setup_collator_candidates::<T>(T::MinCollators::get(), None);
let collator = candidates[0].clone();
fill_delegators::<T>(n, collator.clone(), 0u32);

// stand up the payout state for round 1: snapshot, one authored block, delayed info
let round: u32 = 1;
let state = CandidatePool::<T>::get(&collator).unwrap();
let total_stake = state.total;
AtStake::<T>::insert(round, &collator, state);
CollatorBlocks::<T>::insert(round, &collator, 1u32);

// fund the pot generously so every delegator earns a nonzero (restakeable) reward
let issuance = T::MinCollatorCandidateStake::get();
let pot = Pallet::<T>::account_id();
T::Currency::make_free_balance_be(&pot, issuance.saturating_add(issuance));
DelayedPayoutInfo::<T>::put(crate::types::DelayedPayoutInfoT {
round,
total_stake,
total_issuance: issuance,
});
// current round must be non-zero, else payout_collator early-returns
Round::<T>::mutate(|r| { r.current = round + 1; });
}: {
Pallet::<T>::payout_collator();
}
verify {
// the collator's snapshot was consumed (its delegators were paid + restaked)
assert!(AtStake::<T>::get(round, &collator).is_none());
}
}

impl_benchmark_test_suite!(
Expand Down
Loading
Loading