pallet_im_online/
migration.rs1use super::*;
21use alloc::vec::Vec;
22use frame_support::{storage_alias, traits::OnRuntimeUpgrade};
23
24#[cfg(feature = "try-runtime")]
25use frame_support::ensure;
26#[cfg(feature = "try-runtime")]
27use sp_runtime::TryRuntimeError;
28
29const TARGET: &str = "runtime::im-online::migration::v1";
31
32mod v0 {
34 use super::*;
35 use frame_support::traits::WrapperOpaque;
36
37 #[derive(Encode, Decode, Default)]
38 pub(super) struct BoundedOpaqueNetworkState {
39 pub peer_id: Vec<u8>,
41 pub external_addresses: Vec<Vec<u8>>,
43 }
44
45 #[storage_alias]
46 pub(super) type ReceivedHeartbeats<T: Config> = StorageDoubleMap<
47 Pallet<T>,
48 Twox64Concat,
49 SessionIndex,
50 Twox64Concat,
51 AuthIndex,
52 WrapperOpaque<BoundedOpaqueNetworkState>,
53 >;
54}
55
56pub mod v1 {
57 use super::*;
58
59 pub struct Migration<T>(core::marker::PhantomData<T>);
61
62 impl<T: Config> OnRuntimeUpgrade for Migration<T> {
63 #[cfg(feature = "try-runtime")]
64 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
65 let count = v0::ReceivedHeartbeats::<T>::iter().count();
66 log::info!(target: TARGET, "Migrating {} received heartbeats", count);
67
68 Ok((count as u32).encode())
69 }
70
71 fn on_runtime_upgrade() -> Weight {
72 let mut weight = T::DbWeight::get().reads(1);
73 if StorageVersion::get::<Pallet<T>>() != 0 {
74 log::warn!(
75 target: TARGET,
76 "Skipping migration because in-code storage version is not 0"
77 );
78 return weight
79 }
80
81 let heartbeats = v0::ReceivedHeartbeats::<T>::drain().collect::<Vec<_>>();
82
83 weight.saturating_accrue(T::DbWeight::get().reads(heartbeats.len() as u64));
84 weight.saturating_accrue(T::DbWeight::get().writes(heartbeats.len() as u64));
85
86 for (session_index, auth_index, _) in heartbeats {
87 log::trace!(
88 target: TARGET,
89 "Migrated received heartbeat for {:?}...",
90 (session_index, auth_index)
91 );
92 crate::ReceivedHeartbeats::<T>::insert(session_index, auth_index, true);
93 }
94
95 StorageVersion::new(1).put::<Pallet<T>>();
96 weight.saturating_add(T::DbWeight::get().writes(1))
97 }
98
99 #[cfg(feature = "try-runtime")]
100 fn post_upgrade(state: Vec<u8>) -> DispatchResult {
101 let old_received_heartbeats: u32 =
102 Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
103 let new_received_heartbeats = crate::ReceivedHeartbeats::<T>::iter().count();
104
105 if new_received_heartbeats != old_received_heartbeats as usize {
106 log::error!(
107 target: TARGET,
108 "migrated {} received heartbeats, expected {}",
109 new_received_heartbeats,
110 old_received_heartbeats
111 );
112 }
113 ensure!(StorageVersion::get::<Pallet<T>>() >= 1, "must upgrade");
114
115 Ok(())
116 }
117 }
118}
119
120pub fn clear_offchain_storage(validator_set_size: u32) {
125 (0..validator_set_size).for_each(|idx| {
126 let key = {
127 let mut key = DB_PREFIX.to_vec();
128 key.extend(idx.encode());
129 key
130 };
131 sp_runtime::offchain::storage::StorageValueRef::persistent(&key).clear();
132 });
133}
134
135#[cfg(all(feature = "try-runtime", test))]
136mod test {
137 use super::*;
138 use crate::mock::{new_test_ext, Runtime as T};
139 use frame_support::traits::WrapperOpaque;
140
141 #[test]
142 fn migration_works() {
143 new_test_ext().execute_with(|| {
144 assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
145
146 let current_session = <T as pallet::Config>::ValidatorSet::session_index();
148 v0::ReceivedHeartbeats::<T>::insert(
149 ¤t_session,
150 0,
151 WrapperOpaque(v0::BoundedOpaqueNetworkState::default()),
152 );
153 v0::ReceivedHeartbeats::<T>::insert(
154 ¤t_session,
155 1,
156 WrapperOpaque(v0::BoundedOpaqueNetworkState::default()),
157 );
158
159 assert_eq!(v0::ReceivedHeartbeats::<T>::iter().count(), 2);
161 assert_eq!(crate::ReceivedHeartbeats::<T>::iter().count(), 0, "V1 storage corrupted");
162
163 let state = v1::Migration::<T>::pre_upgrade().unwrap();
165 let _w = v1::Migration::<T>::on_runtime_upgrade();
166 v1::Migration::<T>::post_upgrade(state).unwrap();
167
168 assert_eq!(v0::ReceivedHeartbeats::<T>::iter().count(), 0);
170 assert_eq!(crate::ReceivedHeartbeats::<T>::iter().count(), 2);
171 assert!(crate::ReceivedHeartbeats::<T>::contains_key(¤t_session, 0));
172 assert_eq!(Some(true), crate::ReceivedHeartbeats::<T>::get(¤t_session, 1));
173
174 assert_eq!(StorageVersion::get::<Pallet<T>>(), 1);
175 });
176 }
177}