referrerpolicy=no-referrer-when-downgrade

pallet_bridge_relayers/
migration.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! A module that is responsible for migration of storage.
18
19use alloc::vec::Vec;
20use frame_support::{
21	traits::{Get, StorageVersion},
22	weights::Weight,
23};
24
25/// The in-code storage version.
26pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
27
28/// This module contains data structures that are valid for the initial state of `0`.
29/// (used with v1 migration).
30pub 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	/// Structure used to identify the account that pays a reward to the relayer.
41	#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
42	pub struct RewardsAccountParams<LaneId> {
43		/// lane_id
44		pub lane_id: LaneId,
45		/// bridged_chain_id
46		pub bridged_chain_id: ChainId,
47		/// owner
48		pub owner: RewardsAccountOwner,
49	}
50
51	impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
52		/// Create a new instance of `RewardsAccountParams`.
53		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	/// Reward account generator for `v0`.
104	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		/// Return account that pays rewards based on the provided parameters.
111		pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Account {
112			params.into_sub_account_truncating(b"rewards-account")
113		}
114	}
115}
116
117/// This migration updates `RelayerRewards` where `RewardsAccountParams` was used as the key with
118/// `lane_id` as the first attribute, which affects `into_sub_account_truncating`. We are migrating
119/// this key to use the new `RewardsAccountParams` where `lane_id` is the last attribute.
120pub 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	// Copy of `Pallet::<T, I>::register_relayer_reward` compatible with v1.
171	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	/// Migrates the pallet storage to v1.
206	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			// list all rewards (we cannot do this as one step because of `drain` limitation)
218			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			// re-register rewards with new format of `RewardsAccountParams`.
226			for (key1, key2, reward) in rewards_to_migrate {
227				// expand old key
228				let v0::RewardsAccountParams { owner, lane_id, bridged_chain_id } = key2;
229
230				// re-register reward
231				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			// collect actual rewards
249			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			// collect migrated rewards
283			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	/// [`UncheckedMigrationV0ToV1`] wrapped in a
312	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the
313	/// migration is only performed when on-chain version is 0.
314	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
323/// The pallet in version 1 only supported rewards collected under the key of
324/// `RewardsAccountParams`. This migration essentially converts existing `RewardsAccountParams` keys
325/// to the generic type `T::Reward`.
326pub 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	/// Migrates the pallet storage to v2.
337	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			// list all rewards (we cannot do this as one step because of `drain` limitation)
351			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			// re-register rewards with new format.
359			for (key1, key2, reward) in rewards_to_migrate {
360				// convert old key to the new
361				let new_key2: T::Reward = key2.into();
362
363				// re-register reward (drained above)
364				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			// collect actual rewards
378			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			// collect migrated rewards
413			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	/// [`UncheckedMigrationV1ToV2`] wrapped in a
442	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the
443	/// migration is only performed when on-chain version is 1.
444	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}