fix: bound rewards window processing per block

This commit is contained in:
Ahmad Kaouk 2026-03-31 15:03:56 +02:00
parent e7474c92ee
commit a5c3266c5f
4 changed files with 202 additions and 134 deletions

View file

@ -23,7 +23,7 @@ use crate::Pallet as ExternalValidatorsRewards;
use {
crate::types::BenchmarkHelper,
frame_benchmarking::{account, v2::*, BenchmarkError},
frame_support::traits::{Currency, EnsureOrigin},
frame_support::traits::{Currency, EnsureOrigin, Hooks},
};
const SEED: u32 = 0;
@ -108,12 +108,44 @@ mod benchmarks {
// on_initialize: unsent queue is empty (2 reads for head+tail)
#[benchmark]
fn process_unsent_reward_eras_empty() -> Result<(), BenchmarkError> {
// Ensure queue is empty (default state: head == tail == 0)
assert!(ExternalValidatorsRewards::<T>::unsent_queue_is_empty());
// Exercise the "empty slot at head" fallback path that returns the empty weight.
<UnsentRewardHead<T>>::put(0);
<UnsentRewardTail<T>>::put(1);
frame_system::Pallet::<T>::set_block_number(1u32.into());
#[block]
{
ExternalValidatorsRewards::<T>::process_unsent_reward_eras();
<ExternalValidatorsRewards<T> as Hooks<
frame_system::pallet_prelude::BlockNumberFor<T>,
>>::on_initialize(1u32.into());
}
Ok(())
}
#[benchmark]
fn process_closed_windows_idle() -> Result<(), BenchmarkError> {
pallet_timestamp::Now::<T>::put(35_000u64);
#[block]
{
ExternalValidatorsRewards::<T>::process_closed_windows(35, 0, 10);
}
Ok(())
}
#[benchmark]
fn process_closed_windows_processed() -> Result<(), BenchmarkError> {
frame_system::Pallet::<T>::set_block_number(0u32.into());
T::BenchmarkHelper::setup();
setup_window_reward_state::<T>(20, 42);
<NextWindowToSubmit<T>>::put(20);
pallet_timestamp::Now::<T>::put(35_000u64);
#[block]
{
ExternalValidatorsRewards::<T>::process_closed_windows(35, 0, 10);
}
Ok(())
@ -123,10 +155,13 @@ mod benchmarks {
#[benchmark]
fn process_unsent_reward_eras_expired() -> Result<(), BenchmarkError> {
push_unsent_entry::<T>(999, 99, 10);
frame_system::Pallet::<T>::set_block_number(1u32.into());
#[block]
{
ExternalValidatorsRewards::<T>::process_unsent_reward_eras();
<ExternalValidatorsRewards<T> as Hooks<
frame_system::pallet_prelude::BlockNumberFor<T>,
>>::on_initialize(1u32.into());
}
// Entry should have been removed
@ -143,10 +178,13 @@ mod benchmarks {
setup_window_reward_state::<T>(0, 42);
push_unsent_entry::<T>(0, 0, 10);
frame_system::Pallet::<T>::set_block_number(1u32.into());
#[block]
{
ExternalValidatorsRewards::<T>::process_unsent_reward_eras();
<ExternalValidatorsRewards<T> as Hooks<
frame_system::pallet_prelude::BlockNumberFor<T>,
>>::on_initialize(1u32.into());
}
assert!(ExternalValidatorsRewards::<T>::unsent_queue_is_empty());
@ -162,10 +200,13 @@ mod benchmarks {
setup_window_reward_state::<T>(0, 42);
push_unsent_entry::<T>(0, 0, 10);
frame_system::Pallet::<T>::set_block_number(1u32.into());
#[block]
{
ExternalValidatorsRewards::<T>::process_unsent_reward_eras();
<ExternalValidatorsRewards<T> as Hooks<
frame_system::pallet_prelude::BlockNumberFor<T>,
>>::on_initialize(1u32.into());
}
Ok(())

View file

@ -193,7 +193,16 @@ pub mod pallet {
#[pallet::hooks]
impl<T: Config> Hooks<frame_system::pallet_prelude::BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(_n: frame_system::pallet_prelude::BlockNumberFor<T>) -> Weight {
Self::process_unsent_reward_eras()
let head = UnsentRewardHead::<T>::get();
let tail = UnsentRewardTail::<T>::get();
if head != tail {
return Self::process_unsent_reward_eras_from(head, tail);
}
let (genesis, interval) = Self::rewards_window_config();
let now = Self::now_seconds();
Self::process_closed_windows(now, genesis, interval)
}
fn on_runtime_upgrade() -> Weight {
@ -210,9 +219,6 @@ pub mod pallet {
// in-flight era is not partially accounted under mixed semantics.
WindowModeStartsAtEra::<T>::put(cutover_era);
NextWindowToSubmit::<T>::kill();
for slot in 0..UNSENT_QUEUE_CAPACITY {
UnsentRewardWindow::<T>::remove(slot);
}
UnsentRewardHead::<T>::put(0);
UnsentRewardTail::<T>::put(0);
STORAGE_VERSION.put::<Pallet<T>>();
@ -223,7 +229,7 @@ pub mod pallet {
cutover_era,
);
T::DbWeight::get().reads_writes(2, 69)
T::DbWeight::get().reads_writes(2, 5)
}
#[cfg(feature = "try-runtime")]
@ -257,7 +263,7 @@ pub mod pallet {
"Window cutover era was not initialized correctly",
);
frame_support::ensure!(
NextWindowToSubmit::<T>::get() == 0,
NextWindowToSubmit::<T>::get().is_none(),
"NextWindowToSubmit was not reset",
);
frame_support::ensure!(
@ -268,10 +274,6 @@ pub mod pallet {
UnsentRewardTail::<T>::get() == 0,
"UnsentRewardTail was not reset",
);
frame_support::ensure!(
UnsentRewardWindow::<T>::iter().next().is_none(),
"UnsentRewardWindow entries were not cleared",
);
Ok(())
}
@ -429,7 +431,7 @@ pub mod pallet {
/// Pointer to the next window start to submit.
#[pallet::storage]
pub type NextWindowToSubmit<T: Config> = StorageValue<_, u32, ValueQuery>;
pub type NextWindowToSubmit<T: Config> = StorageValue<_, u32, OptionQuery>;
/// Era at which window mode becomes active after a live upgrade.
/// `0` means window mode is active immediately (fresh chains/tests).
@ -495,19 +497,6 @@ pub mod pallet {
WindowOperatorPoints::<T>::remove(window_start);
}
fn earliest_window_with_state() -> Option<u32> {
let mut earliest = None;
for window in WindowOperatorPoints::<T>::iter_keys() {
earliest = Some(earliest.map_or(window, |current: u32| current.min(window)));
}
for window in WindowInflationAmount::<T>::iter_keys() {
earliest = Some(earliest.map_or(window, |current: u32| current.min(window)));
}
earliest
}
fn window_rewards_info(
window_start: u32,
window_index: u32,
@ -538,12 +527,21 @@ pub mod pallet {
genesis: u32,
interval: u32,
) {
if inflation_amount == 0 || era_end <= era_start {
if era_end <= era_start {
return;
}
let mut window_start = Self::window_start_for(era_start, genesis, interval);
if NextWindowToSubmit::<T>::get().is_none() {
NextWindowToSubmit::<T>::put(window_start);
}
if inflation_amount == 0 {
return;
}
let era_duration = era_end.saturating_sub(era_start).max(1);
let mut window_start = Self::window_start_for(era_start, genesis, interval);
let mut allocated = 0u128;
let mut last_window = None;
@ -579,85 +577,85 @@ pub mod pallet {
}
}
fn process_closed_windows(now: u32, genesis: u32, interval: u32) {
let mut next_window = NextWindowToSubmit::<T>::get();
pub(crate) fn process_closed_windows(now: u32, genesis: u32, interval: u32) -> Weight {
let Some(mut next_window) = NextWindowToSubmit::<T>::get() else {
return T::WeightInfo::process_closed_windows_idle();
};
if next_window == 0 {
next_window = Self::earliest_window_with_state().unwrap_or_else(|| {
Self::window_start_for(now.saturating_sub(interval), genesis, interval)
});
if next_window.saturating_add(interval) > now {
return T::WeightInfo::process_closed_windows_idle();
}
while next_window.saturating_add(interval) <= now {
let inflation_amount = WindowInflationAmount::<T>::get(next_window);
let operator_points = WindowOperatorPoints::<T>::get(next_window);
let total_points: u128 = operator_points.values().map(|p| *p as u128).sum();
let window_index = Self::window_index_for(next_window, genesis, interval);
let inflation_amount = WindowInflationAmount::<T>::get(next_window);
let operator_points = WindowOperatorPoints::<T>::get(next_window);
let total_points: u128 = operator_points.values().map(|p| *p as u128).sum();
let window_index = Self::window_index_for(next_window, genesis, interval);
if total_points == 0 || inflation_amount == 0 {
Self::clear_window(next_window);
Self::deposit_event(Event::RewardsWindowSkipped {
if total_points == 0 || inflation_amount == 0 {
Self::clear_window(next_window);
Self::deposit_event(Event::RewardsWindowSkipped {
window_start: next_window,
window_index,
});
next_window = next_window.saturating_add(interval);
NextWindowToSubmit::<T>::put(next_window);
return T::WeightInfo::process_closed_windows_processed();
}
let utils = RewardsPeriodUtils {
period_index: window_index,
period_start: next_window,
duration: interval,
total_points,
individual_points: operator_points.into_iter().collect(),
inflation_amount,
};
match Self::send_rewards_message(&utils) {
Some(message_id) => {
Self::deposit_event(Event::RewardsWindowSubmitted {
message_id,
window_start: next_window,
window_index,
total_points,
inflation_amount,
});
}
None => {
Self::deposit_event(Event::RewardsWindowSubmissionFailed {
window_start: next_window,
window_index,
});
next_window = next_window.saturating_add(interval);
continue;
}
let utils = RewardsPeriodUtils {
period_index: window_index,
period_start: next_window,
duration: interval,
total_points,
individual_points: operator_points.into_iter().collect(),
inflation_amount,
};
let queued_window = QueuedRewardsWindow {
window_start: next_window,
window_index,
duration: interval,
};
match Self::send_rewards_message(&utils) {
Some(message_id) => {
Self::deposit_event(Event::RewardsWindowSubmitted {
message_id,
window_start: next_window,
window_index,
total_points,
inflation_amount,
});
if Self::unsent_queue_push(queued_window) {
next_window = next_window.saturating_add(interval);
NextWindowToSubmit::<T>::put(next_window);
return T::WeightInfo::process_closed_windows_processed();
}
None => {
Self::deposit_event(Event::RewardsWindowSubmissionFailed {
window_start: next_window,
window_index,
});
let queued_window = QueuedRewardsWindow {
window_start: next_window,
window_index,
duration: interval,
};
if Self::unsent_queue_push(queued_window) {
next_window = next_window.saturating_add(interval);
continue;
}
log::error!(
target: "ext_validators_rewards",
"Unsent reward queue full, cannot enqueue window {}",
next_window,
);
Self::deposit_event(Event::UnsentWindowQueueFull {
window_start: next_window,
window_index,
});
break;
}
log::error!(
target: "ext_validators_rewards",
"Unsent reward queue full, cannot enqueue window {}",
next_window,
);
Self::deposit_event(Event::UnsentWindowQueueFull {
window_start: next_window,
window_index,
});
return T::WeightInfo::process_closed_windows_processed();
}
Self::clear_window(next_window);
next_window = next_window.saturating_add(interval);
}
Self::clear_window(next_window);
next_window = next_window.saturating_add(interval);
NextWindowToSubmit::<T>::put(next_window);
T::WeightInfo::process_closed_windows_processed()
}
/// Reward validators. Does not check if the validators are valid, caller needs to make sure of that.
@ -794,10 +792,7 @@ pub mod pallet {
/// Process at most one unsent reward window per block.
/// On failure the head pointer advances to the next entry so a single
/// stuck window does not block retries for subsequent windows.
pub(crate) fn process_unsent_reward_eras() -> Weight {
let head = UnsentRewardHead::<T>::get();
let tail = UnsentRewardTail::<T>::get();
fn process_unsent_reward_eras_from(head: u32, tail: u32) -> Weight {
if head == tail {
return T::WeightInfo::process_unsent_reward_eras_empty();
}
@ -1284,8 +1279,6 @@ pub mod pallet {
T::WeightInfo::on_era_end(),
DispatchClass::Mandatory,
);
Self::process_closed_windows(now, genesis, interval);
}
}
}

View file

@ -25,6 +25,13 @@ use {
sp_core::H160,
};
fn run_rewards_initialize() {
let next_block = System::block_number() + 1;
System::set_block_number(next_block);
Timestamp::set_timestamp(Timestamp::get() + BLOCK_TIME);
let _ = <crate::Pallet<Test> as Hooks<u64>>::on_initialize(next_block);
}
#[test]
fn basic_setup_works() {
new_test_ext().execute_with(|| {
@ -82,22 +89,13 @@ fn runtime_upgrade_schedules_window_mode_for_next_era_and_resets_retry_state() {
StorageVersion::new(1).put::<crate::Pallet<Test>>();
crate::WindowModeStartsAtEra::<Test>::put(0);
crate::NextWindowToSubmit::<Test>::put(123);
crate::UnsentRewardWindow::<Test>::insert(
5,
crate::QueuedRewardsWindow {
window_start: 100,
window_index: 10,
duration: 10,
},
);
crate::UnsentRewardHead::<Test>::put(5);
crate::UnsentRewardTail::<Test>::put(9);
let _ = <crate::Pallet<Test> as Hooks<u64>>::on_runtime_upgrade();
assert_eq!(crate::WindowModeStartsAtEra::<Test>::get(), 8);
assert_eq!(crate::NextWindowToSubmit::<Test>::get(), 0);
assert_eq!(crate::UnsentRewardWindow::<Test>::iter().count(), 0);
assert_eq!(crate::NextWindowToSubmit::<Test>::get(), None);
assert_eq!(crate::UnsentRewardHead::<Test>::get(), 0);
assert_eq!(crate::UnsentRewardTail::<Test>::get(), 0);
assert_eq!(
@ -188,7 +186,7 @@ fn transition_era_on_era_end_is_skipped_before_cutover() {
"transition era should not emit window-processing events before cutover"
);
assert_eq!(crate::WindowInflationAmount::<Test>::iter().count(), 0);
assert_eq!(crate::NextWindowToSubmit::<Test>::get(), 0);
assert_eq!(crate::NextWindowToSubmit::<Test>::get(), None);
})
}
@ -246,6 +244,8 @@ fn window_mode_submits_closed_windows_and_advances_pointer() {
run_to_block(15); // now = 45s, window [30,40) is closed
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
run_rewards_initialize();
// Inflation is 42 (default). After 20% treasury split: rewards_amount = 34.
// Era spans 20s to 45s = 25s. Window [20,30) overlap = 10s, [30,40) overlap = 10s,
@ -267,7 +267,7 @@ fn window_mode_submits_closed_windows_and_advances_pointer() {
assert_eq!(
pallet_external_validators_rewards::NextWindowToSubmit::<Test>::get(),
40
Some(40)
);
assert!(
pallet_external_validators_rewards::WindowOperatorPoints::<Test>::get(30).is_empty()
@ -311,7 +311,7 @@ fn window_mode_era_inflation_split_across_multiple_windows() {
ExternalValidatorsRewards::note_block_author(H160::from_low_u64_be(1));
}
// on_era_end allocates inflation across windows then processes closed ones.
// on_era_end allocates inflation across windows; on_initialize submits closed ones.
// Era spans from 5s to now=35s (30s duration).
// Window overlaps:
// [0,10): overlap [5,10) = 5s → 5/30 of inflation
@ -319,6 +319,9 @@ fn window_mode_era_inflation_split_across_multiple_windows() {
// [20,30): overlap [20,30) = 10s → 10/30 of inflation
// [30,40): overlap [30,35) = 5s → 5/30 of inflation (current, not closed)
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
run_rewards_initialize();
run_rewards_initialize();
// on_era_end allocates mint_result.rewards_amount (post-treasury split) to windows.
// Inflation = 42, treasury = 20% of 42 = 8, rewards_amount = 34.
@ -327,7 +330,7 @@ fn window_mode_era_inflation_split_across_multiple_windows() {
let treasury_amount = InflationTreasuryProportion::get().mul_floor(inflation);
let rewards_amount = inflation - treasury_amount;
// Closed windows are cleared by process_closed_windows, so verify via events.
// Closed windows are cleared one per block, so verify via events after draining them.
let events = System::events();
let expected_w0 = rewards_amount * 5 / 30;
@ -431,6 +434,9 @@ fn window_mode_submits_multiple_closed_windows_in_single_era_end() {
// Window [60,70) is current.
run_to_block(35); // now = 65s
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
run_rewards_initialize();
run_rewards_initialize();
let events = System::events();
@ -475,7 +481,7 @@ fn window_mode_submits_multiple_closed_windows_in_single_era_end() {
// NextWindowToSubmit should advance past all closed windows
assert_eq!(
pallet_external_validators_rewards::NextWindowToSubmit::<Test>::get(),
60
Some(60)
);
// All closed windows should have their storage cleared
@ -522,6 +528,8 @@ fn window_mode_delivery_failure_emits_submission_failed_event() {
// Advance to block 15 (now=45s), window [30,40) is closed
run_to_block(15); // now = 45s
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
run_rewards_initialize();
let events = System::events();
@ -563,7 +571,7 @@ fn window_mode_delivery_failure_emits_submission_failed_event() {
// NextWindowToSubmit should still advance
assert_eq!(
pallet_external_validators_rewards::NextWindowToSubmit::<Test>::get(),
40
Some(40)
);
assert_eq!(unsent_len(), 1, "failed window should be queued for retry");
})
@ -660,6 +668,7 @@ fn test_on_era_end() {
run_to_block(10);
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
let inflation =
<Test as pallet_external_validators_rewards::Config>::EraInflationProvider::get();
@ -4285,6 +4294,7 @@ fn send_failure_emits_window_submission_failed_and_queues_window() {
System::reset_events();
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
// Verify window submission failure event
let window_index = window_start.saturating_sub(genesis) / window_duration;
@ -4333,6 +4343,7 @@ fn failed_window_is_retried_on_initialize() {
Timestamp::set_timestamp(((window_start + duration + 1) as u64) * 1000);
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
assert_eq!(unsent_len(), 1);
@ -4340,7 +4351,7 @@ fn failed_window_is_retried_on_initialize() {
// Sending should succeed (send_message_fails is false by default)
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
// Queue should be empty
assert!(unsent_is_empty());
@ -4381,7 +4392,7 @@ fn on_initialize_retries_and_succeeds() {
push_unsent(window_start, window_index, duration);
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert!(unsent_is_empty());
@ -4414,18 +4425,18 @@ fn on_initialize_moves_failed_entry_to_back() {
push_unsent(40, 4, duration);
// First call: tries window 30, fails, moves it to the back of the queue
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert_eq!(unsent_len(), 2);
// Second call: tries window 40, fails, moves it to the back
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert_eq!(unsent_len(), 2);
Mock::mutate(|mock| mock.send_message_fails = false);
// Third call: window 30 (now at front again), succeeds
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert_eq!(unsent_len(), 1);
System::assert_has_event(RuntimeEvent::ExternalValidatorsRewards(
crate::Event::RewardsWindowRetried {
@ -4438,7 +4449,7 @@ fn on_initialize_moves_failed_entry_to_back() {
));
// Fourth call: window 40 succeeds
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert!(unsent_is_empty());
})
}
@ -4452,7 +4463,7 @@ fn on_initialize_removes_expired_window() {
push_unsent(999, 99, duration);
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert!(unsent_is_empty());
@ -4471,7 +4482,7 @@ fn on_initialize_noop_when_queue_empty() {
run_to_block(1);
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
// No events should be emitted
let events = System::events();
@ -4495,7 +4506,7 @@ fn on_initialize_processes_only_head() {
push_unsent(40, 4, duration);
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert_eq!(unsent_len(), 1);
})
@ -4602,10 +4613,11 @@ fn send_failure_retains_window_and_advances_pointer() {
System::reset_events();
ExternalValidatorsRewards::on_era_end(1);
run_rewards_initialize();
let next_window = crate::NextWindowToSubmit::<Test>::get();
assert!(
next_window > window_start,
next_window == Some(window_start + window_duration),
"NextWindowToSubmit should advance past the failed window"
);
@ -4681,15 +4693,15 @@ fn head_of_line_blocking_avoided() {
Mock::mutate(|mock| mock.send_message_fails = true);
// Block 1: tries window 30, fails, advances head → window 40
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
// Block 2: tries window 40, fails, advances head → window 50
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
Mock::mutate(|mock| mock.send_message_fails = false);
// Block 3: tries window 50, succeeds
System::reset_events();
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
System::assert_has_event(RuntimeEvent::ExternalValidatorsRewards(
crate::Event::RewardsWindowRetried {
message_id: Default::default(),
@ -4701,7 +4713,7 @@ fn head_of_line_blocking_avoided() {
));
// Block 4: wraps around to window 30, succeeds
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
System::assert_has_event(RuntimeEvent::ExternalValidatorsRewards(
crate::Event::RewardsWindowRetried {
message_id: Default::default(),
@ -4713,7 +4725,7 @@ fn head_of_line_blocking_avoided() {
));
// Block 5: window 40 succeeds
ExternalValidatorsRewards::process_unsent_reward_eras();
run_rewards_initialize();
assert!(unsent_is_empty());
})
}

View file

@ -54,6 +54,8 @@ use core::marker::PhantomData;
/// Weight functions needed for pallet_external_validators_rewards.
pub trait WeightInfo {
fn on_era_end() -> Weight;
fn process_closed_windows_idle() -> Weight;
fn process_closed_windows_processed() -> Weight;
fn process_unsent_reward_eras_empty() -> Weight;
fn process_unsent_reward_eras_expired() -> Weight;
fn process_unsent_reward_eras_success() -> Weight;
@ -90,6 +92,17 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().writes(5_u64))
}
fn process_closed_windows_idle() -> Weight {
Weight::from_parts(10_000_000, 0)
.saturating_add(T::DbWeight::get().reads(3_u64))
}
fn process_closed_windows_processed() -> Weight {
// Use the existing resend success path as an upper bound for a single
// closed-window submission/skip attempt.
Self::process_unsent_reward_eras_success()
}
fn process_unsent_reward_eras_empty() -> Weight {
// 1 read for UnsentRewardEras
Weight::from_parts(5_000_000, 0)
@ -149,6 +162,15 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().writes(5_u64))
}
fn process_closed_windows_idle() -> Weight {
Weight::from_parts(10_000_000, 0)
.saturating_add(RocksDbWeight::get().reads(3_u64))
}
fn process_closed_windows_processed() -> Weight {
Self::process_unsent_reward_eras_success()
}
fn process_unsent_reward_eras_empty() -> Weight {
Weight::from_parts(5_000_000, 0)
.saturating_add(RocksDbWeight::get().reads(1_u64))