1use alloc::vec::Vec;
20use frame_support::{
21 traits::{Get, StorageVersion},
22 weights::Weight,
23};
24
25pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
27
28pub mod v0 {
31 use crate::{Config, Pallet};
32 use bp_relayers::RewardsAccountOwner;
33 use bp_runtime::{ChainId, StorageDoubleMapKeyProvider};
34 use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
35 use core::marker::PhantomData;
36 use frame_support::{pallet_prelude::OptionQuery, Blake2_128Concat, Identity};
37 use scale_info::TypeInfo;
38 use sp_runtime::traits::AccountIdConversion;
39
40 #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
42 pub struct RewardsAccountParams<LaneId> {
43 pub lane_id: LaneId,
45 pub bridged_chain_id: ChainId,
47 pub owner: RewardsAccountOwner,
49 }
50
51 impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
52 pub const fn new(
54 lane_id: LaneId,
55 bridged_chain_id: ChainId,
56 owner: RewardsAccountOwner,
57 ) -> Self {
58 Self { lane_id, bridged_chain_id, owner }
59 }
60 }
61
62 impl<LaneId> sp_runtime::TypeId for RewardsAccountParams<LaneId> {
63 const TYPE_ID: [u8; 4] = *b"brap";
64 }
65
66 pub(crate) struct RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>(
67 PhantomData<(AccountId, RewardBalance, LaneId)>,
68 );
69
70 impl<AccountId, RewardBalance, LaneId> StorageDoubleMapKeyProvider
71 for RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>
72 where
73 AccountId: 'static + Codec + EncodeLike + Send + Sync,
74 RewardBalance: 'static + Codec + EncodeLike + Send + Sync,
75 LaneId: Codec + EncodeLike + Send + Sync,
76 {
77 const MAP_NAME: &'static str = "RelayerRewards";
78
79 type Hasher1 = Blake2_128Concat;
80 type Key1 = AccountId;
81 type Hasher2 = Identity;
82 type Key2 = RewardsAccountParams<LaneId>;
83 type Value = RewardBalance;
84 }
85
86 pub(crate) type RelayerRewardsKeyProviderOf<T, I, LaneId> = RelayerRewardsKeyProvider<
87 <T as frame_system::Config>::AccountId,
88 <T as Config<I>>::RewardBalance,
89 LaneId,
90 >;
91
92 #[frame_support::storage_alias]
93 pub(crate) type RelayerRewards<T: Config<I>, I: 'static, LaneId> = StorageDoubleMap<
94 Pallet<T, I>,
95 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher1,
96 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key1,
97 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher2,
98 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key2,
99 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Value,
100 OptionQuery,
101 >;
102
103 pub struct PayRewardFromAccount<Account, LaneId>(PhantomData<(Account, LaneId)>);
105 impl<Account, LaneId> PayRewardFromAccount<Account, LaneId>
106 where
107 Account: Decode + Encode,
108 LaneId: Decode + Encode,
109 {
110 pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Account {
112 params.into_sub_account_truncating(b"rewards-account")
113 }
114 }
115}
116
117pub mod v1 {
121 use super::*;
122 use crate::{Config, Pallet};
123 use bp_messages::LaneIdType;
124 use bp_relayers::RewardsAccountParams;
125 use bp_runtime::StorageDoubleMapKeyProvider;
126 use codec::{Codec, EncodeLike};
127 use core::marker::PhantomData;
128 use frame_support::{
129 pallet_prelude::OptionQuery, traits::UncheckedOnRuntimeUpgrade, Blake2_128Concat, Identity,
130 };
131 use sp_arithmetic::traits::Zero;
132
133 pub(crate) struct RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>(
134 PhantomData<(AccountId, RewardBalance, LaneId)>,
135 );
136
137 impl<AccountId, RewardBalance, LaneId> StorageDoubleMapKeyProvider
138 for RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>
139 where
140 AccountId: 'static + Codec + EncodeLike + Send + Sync,
141 RewardBalance: 'static + Codec + EncodeLike + Send + Sync,
142 LaneId: Codec + EncodeLike + Send + Sync,
143 {
144 const MAP_NAME: &'static str = "RelayerRewards";
145
146 type Hasher1 = Blake2_128Concat;
147 type Key1 = AccountId;
148 type Hasher2 = Identity;
149 type Key2 = v1::RewardsAccountParams<LaneId>;
150 type Value = RewardBalance;
151 }
152
153 pub(crate) type RelayerRewardsKeyProviderOf<T, I, LaneId> = RelayerRewardsKeyProvider<
154 <T as frame_system::Config>::AccountId,
155 <T as Config<I>>::RewardBalance,
156 LaneId,
157 >;
158
159 #[frame_support::storage_alias]
160 pub(crate) type RelayerRewards<T: Config<I>, I: 'static, LaneId> = StorageDoubleMap<
161 Pallet<T, I>,
162 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher1,
163 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key1,
164 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher2,
165 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key2,
166 <RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Value,
167 OptionQuery,
168 >;
169
170 fn register_relayer_reward_for_v1<
172 T: Config<I>,
173 I: 'static,
174 LaneId: LaneIdType + Send + Sync,
175 >(
176 rewards_account_params: v1::RewardsAccountParams<LaneId>,
177 relayer: &T::AccountId,
178 reward_balance: T::RewardBalance,
179 ) {
180 use sp_runtime::Saturating;
181
182 if reward_balance.is_zero() {
183 return
184 }
185
186 v1::RelayerRewards::<T, I, LaneId>::mutate(
187 relayer,
188 rewards_account_params,
189 |old_reward: &mut Option<T::RewardBalance>| {
190 let new_reward =
191 old_reward.unwrap_or_else(Zero::zero).saturating_add(reward_balance);
192 *old_reward = Some(new_reward);
193
194 tracing::trace!(
195 target: crate::LOG_TARGET,
196 ?relayer,
197 ?rewards_account_params,
198 ?new_reward,
199 "Relayer can now claim reward"
200 );
201 },
202 );
203 }
204
205 pub struct UncheckedMigrationV0ToV1<T, I, LaneId>(PhantomData<(T, I, LaneId)>);
207
208 #[cfg(feature = "try-runtime")]
209 const LOG_TARGET: &str = "runtime::bridge-relayers-migration";
210
211 impl<T: Config<I>, I: 'static, LaneId: LaneIdType + Send + Sync> UncheckedOnRuntimeUpgrade
212 for UncheckedMigrationV0ToV1<T, I, LaneId>
213 {
214 fn on_runtime_upgrade() -> Weight {
215 let mut weight = T::DbWeight::get().reads(1);
216
217 let mut rewards_to_migrate =
219 Vec::with_capacity(v0::RelayerRewards::<T, I, LaneId>::iter().count());
220 for (key1, key2, reward) in v0::RelayerRewards::<T, I, LaneId>::drain() {
221 rewards_to_migrate.push((key1, key2, reward));
222 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
223 }
224
225 for (key1, key2, reward) in rewards_to_migrate {
227 let v0::RewardsAccountParams { owner, lane_id, bridged_chain_id } = key2;
229
230 register_relayer_reward_for_v1::<T, I, LaneId>(
232 v1::RewardsAccountParams::new(lane_id, bridged_chain_id, owner),
233 &key1,
234 reward,
235 );
236 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
237 }
238
239 weight
240 }
241
242 #[cfg(feature = "try-runtime")]
243 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
244 use codec::Encode;
245 use frame_support::BoundedBTreeMap;
246 use sp_runtime::traits::ConstU32;
247
248 let mut rewards: BoundedBTreeMap<
250 (T::AccountId, LaneId),
251 T::RewardBalance,
252 ConstU32<{ u32::MAX }>,
253 > = BoundedBTreeMap::new();
254 for (key1, key2, reward) in v0::RelayerRewards::<T, I, LaneId>::iter() {
255 tracing::info!(target: LOG_TARGET, ?key1, ?key2, ?reward, "Reward to migrate");
256 rewards = rewards
257 .try_mutate(|inner| {
258 inner
259 .entry((key1.clone(), key2.lane_id))
260 .and_modify(|value| *value += reward)
261 .or_insert(reward);
262 })
263 .unwrap();
264 }
265 tracing::info!(target: LOG_TARGET, ?rewards, "Found total rewards to migrate");
266
267 Ok(rewards.encode())
268 }
269
270 #[cfg(feature = "try-runtime")]
271 fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
272 use codec::Decode;
273 use frame_support::BoundedBTreeMap;
274 use sp_runtime::traits::ConstU32;
275
276 let rewards_before: BoundedBTreeMap<
277 (T::AccountId, LaneId),
278 T::RewardBalance,
279 ConstU32<{ u32::MAX }>,
280 > = Decode::decode(&mut &state[..]).unwrap();
281
282 let mut rewards_after: BoundedBTreeMap<
284 (T::AccountId, LaneId),
285 T::RewardBalance,
286 ConstU32<{ u32::MAX }>,
287 > = BoundedBTreeMap::new();
288 for (key1, key2, reward) in v1::RelayerRewards::<T, I, LaneId>::iter() {
289 tracing::info!(target: LOG_TARGET, ?key1, ?key2, ?reward, "Migrated rewards");
290 rewards_after = rewards_after
291 .try_mutate(|inner| {
292 inner
293 .entry((key1.clone(), *key2.lane_id()))
294 .and_modify(|value| *value += reward)
295 .or_insert(reward);
296 })
297 .unwrap();
298 }
299 tracing::info!(target: LOG_TARGET, ?rewards_after, "Found total migrated rewards");
300
301 frame_support::ensure!(
302 rewards_before == rewards_after,
303 "The rewards were not migrated correctly!."
304 );
305
306 tracing::info!(target: LOG_TARGET, "migrated all.");
307 Ok(())
308 }
309 }
310
311 pub type MigrationToV1<T, I, LaneId> = frame_support::migrations::VersionedMigration<
315 0,
316 1,
317 UncheckedMigrationV0ToV1<T, I, LaneId>,
318 Pallet<T, I>,
319 <T as frame_system::Config>::DbWeight,
320 >;
321}
322
323pub mod v2 {
327 use super::*;
328 #[cfg(feature = "try-runtime")]
329 use crate::RelayerRewards;
330 use crate::{Config, Pallet};
331 use bp_messages::LaneIdType;
332 use bp_relayers::RewardsAccountParams;
333 use core::marker::PhantomData;
334 use frame_support::traits::UncheckedOnRuntimeUpgrade;
335
336 pub struct UncheckedMigrationV1ToV2<T, I, LaneId>(PhantomData<(T, I, LaneId)>);
338
339 #[cfg(feature = "try-runtime")]
340 const LOG_TARGET: &str = "runtime::bridge-relayers-migration";
341
342 impl<T: Config<I>, I: 'static, LaneId: LaneIdType + Send + Sync> UncheckedOnRuntimeUpgrade
343 for UncheckedMigrationV1ToV2<T, I, LaneId>
344 where
345 <T as Config<I>>::Reward: From<RewardsAccountParams<LaneId>>,
346 {
347 fn on_runtime_upgrade() -> Weight {
348 let mut weight = T::DbWeight::get().reads(1);
349
350 let mut rewards_to_migrate =
352 Vec::with_capacity(v1::RelayerRewards::<T, I, LaneId>::iter().count());
353 for (key1, key2, reward) in v1::RelayerRewards::<T, I, LaneId>::drain() {
354 rewards_to_migrate.push((key1, key2, reward));
355 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
356 }
357
358 for (key1, key2, reward) in rewards_to_migrate {
360 let new_key2: T::Reward = key2.into();
362
363 Pallet::<T, I>::register_relayer_reward(new_key2, &key1, reward);
365 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
366 }
367
368 weight
369 }
370
371 #[cfg(feature = "try-runtime")]
372 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
373 use codec::Encode;
374 use frame_support::BoundedBTreeMap;
375 use sp_runtime::traits::ConstU32;
376
377 let mut rewards: BoundedBTreeMap<
379 (T::AccountId, Vec<u8>),
380 T::RewardBalance,
381 ConstU32<{ u32::MAX }>,
382 > = BoundedBTreeMap::new();
383 for (key1, key2, reward) in v1::RelayerRewards::<T, I, LaneId>::iter() {
384 let new_key2: T::Reward = key2.into();
385 tracing::info!(target: LOG_TARGET, ?key1, ?key2, ?new_key2, ?reward, "Reward to migrate");
386 rewards = rewards
387 .try_mutate(|inner| {
388 inner
389 .entry((key1.clone(), new_key2.encode()))
390 .and_modify(|value| *value += reward)
391 .or_insert(reward);
392 })
393 .unwrap();
394 }
395 tracing::info!(target: LOG_TARGET, ?rewards, "Found total rewards to migrate");
396
397 Ok(rewards.encode())
398 }
399
400 #[cfg(feature = "try-runtime")]
401 fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
402 use codec::{Decode, Encode};
403 use frame_support::BoundedBTreeMap;
404 use sp_runtime::traits::ConstU32;
405
406 let rewards_before: BoundedBTreeMap<
407 (T::AccountId, Vec<u8>),
408 T::RewardBalance,
409 ConstU32<{ u32::MAX }>,
410 > = Decode::decode(&mut &state[..]).unwrap();
411
412 let mut rewards_after: BoundedBTreeMap<
414 (T::AccountId, Vec<u8>),
415 T::RewardBalance,
416 ConstU32<{ u32::MAX }>,
417 > = BoundedBTreeMap::new();
418 for (key1, key2, reward) in v2::RelayerRewards::<T, I>::iter() {
419 tracing::info!(target: LOG_TARGET, ?key1, ?key2, ?reward, "Migrated rewards");
420 rewards_after = rewards_after
421 .try_mutate(|inner| {
422 inner
423 .entry((key1.clone(), key2.encode()))
424 .and_modify(|value| *value += reward)
425 .or_insert(reward);
426 })
427 .unwrap();
428 }
429 tracing::info!(target: LOG_TARGET, ?rewards_after, "Found total migrated rewards");
430
431 frame_support::ensure!(
432 rewards_before == rewards_after,
433 "The rewards were not migrated correctly!."
434 );
435
436 tracing::info!(target: LOG_TARGET, "migrated all.");
437 Ok(())
438 }
439 }
440
441 pub type MigrationToV2<T, I, LaneId> = frame_support::migrations::VersionedMigration<
445 1,
446 2,
447 UncheckedMigrationV1ToV2<T, I, LaneId>,
448 Pallet<T, I>,
449 <T as frame_system::Config>::DbWeight,
450 >;
451}