Skip to content

Commit 07d32c3

Browse files
committed
scheduler at 2s
1 parent 9c38d40 commit 07d32c3

2 files changed

Lines changed: 166 additions & 0 deletions

File tree

runtime/hydradx/src/migrations/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515

1616
use crate::Runtime;
1717

18+
pub mod scheduler;
19+
1820
// New migrations which need to be cleaned up after every Runtime upgrade
1921
pub type UnreleasedSingleBlockMigrations = (
2022
pallet_ema_oracle::migrations::v2::MigrateV1ToV2<Runtime, crate::assets::BifrostAccount>,
2123
pallet_staking::migrations::SetTwoSecBlocksSince<Runtime>,
2224
pallet_dca::migrations::MultiplySchedulesPeriodBy3<Runtime>,
25+
scheduler::MigrateSchedulerTo2sBlocks<Runtime>,
2326
);
2427

2528
// These migrations can run on every runtime upgrade
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright (C) 2020-2025 Intergalactic, Limited (GIB).
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use codec::Encode;
5+
use frame_support::{traits::OnRuntimeUpgrade, weights::Weight, BoundedVec};
6+
use pallet_scheduler::{pallet, BlockNumberFor, ScheduledOf};
7+
use sp_core::Get;
8+
use sp_runtime::{traits::BlockNumberProvider, Saturating};
9+
use sp_std::{marker::PhantomData, vec::Vec};
10+
11+
const MIGRATION_DONE_KEY: &[u8] = b"HydrationScheduler2sBlockMigrationDone";
12+
13+
// This migration migrates the Scheduler to 2s block times by multiplying by 3 the spread between
14+
// stored scheduler block numbers and the current block, and by multiplying periodic intervals by 3.
15+
//
16+
// The migration uses a raw storage marker to prevent accidental double execution. Make sure it is
17+
// removed from the Runtime Executive after it has been run.
18+
pub struct MigrateSchedulerTo2sBlocks<T: pallet::Config>(PhantomData<T>);
19+
20+
impl<T: pallet::Config> MigrateSchedulerTo2sBlocks<T> {
21+
fn is_done() -> bool {
22+
sp_io::storage::get(MIGRATION_DONE_KEY).is_some()
23+
}
24+
25+
fn mark_done() {
26+
sp_io::storage::set(MIGRATION_DONE_KEY, &true.encode());
27+
}
28+
29+
fn scale_block(block: BlockNumberFor<T>, current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
30+
let old_spread = block.saturating_sub(current_block);
31+
let new_spread = old_spread.saturating_mul(3u32.into());
32+
current_block.saturating_add(new_spread)
33+
}
34+
}
35+
36+
impl<T: pallet::Config> OnRuntimeUpgrade for MigrateSchedulerTo2sBlocks<T> {
37+
fn on_runtime_upgrade() -> Weight {
38+
if Self::is_done() {
39+
log::warn!("MigrateSchedulerTo2sBlocks already executed");
40+
return T::DbWeight::get().reads(1);
41+
}
42+
43+
let current_block = T::BlockNumberProvider::current_block_number();
44+
let agenda: Vec<(
45+
BlockNumberFor<T>,
46+
BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>,
47+
)> = pallet_scheduler::Agenda::<T>::iter().collect();
48+
let agenda_len = agenda.len() as u64;
49+
50+
let lookup: Vec<_> = pallet_scheduler::Lookup::<T>::iter().collect();
51+
let lookup_len = lookup.len() as u64;
52+
53+
if agenda_len >= 150 {
54+
log::error!("Error: more than 150 agendas exist, len: {:?}", agenda_len);
55+
return T::DbWeight::get().reads_writes(agenda_len.saturating_add(lookup_len).saturating_add(1), 0);
56+
}
57+
58+
// We expect Lookup to be empty on-chain, but migrate up to 5 entries defensively in case
59+
// any named schedules exist at upgrade time. If there are more, skip only Lookup migration.
60+
let migrate_lookup = lookup_len <= 5;
61+
if !migrate_lookup {
62+
log::error!(
63+
"Skipping Scheduler Lookup migration because more than 5 entries exist, len: {:?}",
64+
lookup_len
65+
);
66+
}
67+
68+
for (old_block, mut schedules) in agenda {
69+
for scheduled in schedules.iter_mut().flatten() {
70+
if let Some((period, _remaining)) = scheduled.maybe_periodic.as_mut() {
71+
*period = period.saturating_mul(3u32.into());
72+
}
73+
}
74+
75+
let new_block = Self::scale_block(old_block, current_block);
76+
77+
pallet_scheduler::Agenda::<T>::remove(old_block);
78+
pallet_scheduler::Agenda::<T>::insert(new_block, schedules);
79+
}
80+
81+
let lookup_writes = if migrate_lookup { lookup_len } else { 0 };
82+
if migrate_lookup {
83+
for (name, (block, index)) in lookup {
84+
pallet_scheduler::Lookup::<T>::insert(name, (Self::scale_block(block, current_block), index));
85+
}
86+
}
87+
88+
Self::mark_done();
89+
90+
log::info!(
91+
"MigrateSchedulerTo2sBlocks processed agenda items: {:?}, lookup entries: {:?}, lookup migrated: {:?}",
92+
agenda_len,
93+
lookup_len,
94+
migrate_lookup
95+
);
96+
T::DbWeight::get().reads_writes(
97+
agenda_len.saturating_add(lookup_len).saturating_add(1),
98+
agenda_len
99+
.saturating_mul(2)
100+
.saturating_add(lookup_writes)
101+
.saturating_add(1),
102+
)
103+
}
104+
}
105+
106+
#[cfg(test)]
107+
mod test {
108+
use super::*;
109+
use crate::{Runtime, RuntimeCall, RuntimeOrigin, Scheduler, System};
110+
use frame_support::assert_ok;
111+
112+
#[test]
113+
fn migrate_scheduler_to_2s_blocks_works() {
114+
let mut ext = sp_io::TestExternalities::new_empty();
115+
116+
ext.execute_with(|| {
117+
System::set_block_number(0);
118+
119+
let periodic_call = Box::new(RuntimeCall::System(frame_system::Call::remark_with_event {
120+
remark: vec![1],
121+
}));
122+
let named_call = Box::new(RuntimeCall::System(frame_system::Call::remark_with_event {
123+
remark: vec![2],
124+
}));
125+
let named_id = [7u8; 32];
126+
127+
assert_ok!(Scheduler::schedule(
128+
RuntimeOrigin::root(),
129+
200,
130+
Some((10, 3)),
131+
3,
132+
periodic_call
133+
));
134+
assert_ok!(Scheduler::schedule_named(
135+
RuntimeOrigin::root(),
136+
named_id,
137+
220,
138+
None,
139+
3,
140+
named_call
141+
));
142+
assert!(pallet_scheduler::Agenda::<Runtime>::contains_key(200));
143+
assert!(pallet_scheduler::Agenda::<Runtime>::contains_key(220));
144+
assert_eq!(pallet_scheduler::Lookup::<Runtime>::get(named_id), Some((220, 0)));
145+
146+
System::set_block_number(100);
147+
MigrateSchedulerTo2sBlocks::<Runtime>::on_runtime_upgrade();
148+
149+
assert!(!pallet_scheduler::Agenda::<Runtime>::contains_key(200));
150+
assert!(!pallet_scheduler::Agenda::<Runtime>::contains_key(220));
151+
assert!(pallet_scheduler::Agenda::<Runtime>::contains_key(400));
152+
assert!(pallet_scheduler::Agenda::<Runtime>::contains_key(460));
153+
let migrated_agenda = pallet_scheduler::Agenda::<Runtime>::get(400);
154+
let migrated_schedule = migrated_agenda.get(0).and_then(Option::as_ref).unwrap();
155+
assert_eq!(migrated_schedule.maybe_periodic, Some((30, 3)));
156+
assert_eq!(pallet_scheduler::Lookup::<Runtime>::get(named_id), Some((460, 0)));
157+
158+
MigrateSchedulerTo2sBlocks::<Runtime>::on_runtime_upgrade();
159+
assert!(pallet_scheduler::Agenda::<Runtime>::contains_key(400));
160+
assert!(!pallet_scheduler::Agenda::<Runtime>::contains_key(1000));
161+
})
162+
}
163+
}

0 commit comments

Comments
 (0)