use alloc::vec::Vec;
use frame_support::{
traits::{Get, StorageVersion},
weights::Weight,
};
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
pub mod v0 {
use crate::{Config, Pallet};
use bp_relayers::RewardsAccountOwner;
use bp_runtime::{ChainId, StorageDoubleMapKeyProvider};
use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen};
use core::marker::PhantomData;
use frame_support::{pallet_prelude::OptionQuery, Blake2_128Concat, Identity};
use scale_info::TypeInfo;
use sp_runtime::traits::AccountIdConversion;
#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
pub struct RewardsAccountParams<LaneId> {
pub lane_id: LaneId,
pub bridged_chain_id: ChainId,
pub owner: RewardsAccountOwner,
}
impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
pub const fn new(
lane_id: LaneId,
bridged_chain_id: ChainId,
owner: RewardsAccountOwner,
) -> Self {
Self { lane_id, bridged_chain_id, owner }
}
}
impl<LaneId> sp_runtime::TypeId for RewardsAccountParams<LaneId> {
const TYPE_ID: [u8; 4] = *b"brap";
}
pub(crate) struct RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>(
PhantomData<(AccountId, RewardBalance, LaneId)>,
);
impl<AccountId, RewardBalance, LaneId> StorageDoubleMapKeyProvider
for RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>
where
AccountId: 'static + Codec + EncodeLike + Send + Sync,
RewardBalance: 'static + Codec + EncodeLike + Send + Sync,
LaneId: Codec + EncodeLike + Send + Sync,
{
const MAP_NAME: &'static str = "RelayerRewards";
type Hasher1 = Blake2_128Concat;
type Key1 = AccountId;
type Hasher2 = Identity;
type Key2 = RewardsAccountParams<LaneId>;
type Value = RewardBalance;
}
pub(crate) type RelayerRewardsKeyProviderOf<T, I, LaneId> = RelayerRewardsKeyProvider<
<T as frame_system::Config>::AccountId,
<T as Config<I>>::RewardBalance,
LaneId,
>;
#[frame_support::storage_alias]
pub(crate) type RelayerRewards<T: Config<I>, I: 'static, LaneId> = StorageDoubleMap<
Pallet<T, I>,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher1,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key1,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher2,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key2,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Value,
OptionQuery,
>;
pub struct PayRewardFromAccount<Account, LaneId>(PhantomData<(Account, LaneId)>);
impl<Account, LaneId> PayRewardFromAccount<Account, LaneId>
where
Account: Decode + Encode,
LaneId: Decode + Encode,
{
pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Account {
params.into_sub_account_truncating(b"rewards-account")
}
}
}
pub mod v1 {
use super::*;
use crate::{Config, Pallet};
use bp_messages::LaneIdType;
use bp_relayers::RewardsAccountParams;
use bp_runtime::StorageDoubleMapKeyProvider;
use codec::{Codec, EncodeLike};
use core::marker::PhantomData;
use frame_support::{
pallet_prelude::OptionQuery, traits::UncheckedOnRuntimeUpgrade, Blake2_128Concat, Identity,
};
use sp_arithmetic::traits::Zero;
pub(crate) struct RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>(
PhantomData<(AccountId, RewardBalance, LaneId)>,
);
impl<AccountId, RewardBalance, LaneId> StorageDoubleMapKeyProvider
for RelayerRewardsKeyProvider<AccountId, RewardBalance, LaneId>
where
AccountId: 'static + Codec + EncodeLike + Send + Sync,
RewardBalance: 'static + Codec + EncodeLike + Send + Sync,
LaneId: Codec + EncodeLike + Send + Sync,
{
const MAP_NAME: &'static str = "RelayerRewards";
type Hasher1 = Blake2_128Concat;
type Key1 = AccountId;
type Hasher2 = Identity;
type Key2 = v1::RewardsAccountParams<LaneId>;
type Value = RewardBalance;
}
pub(crate) type RelayerRewardsKeyProviderOf<T, I, LaneId> = RelayerRewardsKeyProvider<
<T as frame_system::Config>::AccountId,
<T as Config<I>>::RewardBalance,
LaneId,
>;
#[frame_support::storage_alias]
pub(crate) type RelayerRewards<T: Config<I>, I: 'static, LaneId> = StorageDoubleMap<
Pallet<T, I>,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher1,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key1,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Hasher2,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Key2,
<RelayerRewardsKeyProviderOf<T, I, LaneId> as StorageDoubleMapKeyProvider>::Value,
OptionQuery,
>;
fn register_relayer_reward_for_v1<
T: Config<I>,
I: 'static,
LaneId: LaneIdType + Send + Sync,
>(
rewards_account_params: v1::RewardsAccountParams<LaneId>,
relayer: &T::AccountId,
reward_balance: T::RewardBalance,
) {
use sp_runtime::Saturating;
if reward_balance.is_zero() {
return
}
v1::RelayerRewards::<T, I, LaneId>::mutate(
relayer,
rewards_account_params,
|old_reward: &mut Option<T::RewardBalance>| {
let new_reward =
old_reward.unwrap_or_else(Zero::zero).saturating_add(reward_balance);
*old_reward = Some(new_reward);
log::trace!(
target: crate::LOG_TARGET,
"Relayer {:?} can now claim reward for serving payer {:?}: {:?}",
relayer,
rewards_account_params,
new_reward,
);
},
);
}
pub struct UncheckedMigrationV0ToV1<T, I, LaneId>(PhantomData<(T, I, LaneId)>);
#[cfg(feature = "try-runtime")]
const LOG_TARGET: &str = "runtime::bridge-relayers-migration";
impl<T: Config<I>, I: 'static, LaneId: LaneIdType + Send + Sync> UncheckedOnRuntimeUpgrade
for UncheckedMigrationV0ToV1<T, I, LaneId>
{
fn on_runtime_upgrade() -> Weight {
let mut weight = T::DbWeight::get().reads(1);
let mut rewards_to_migrate =
Vec::with_capacity(v0::RelayerRewards::<T, I, LaneId>::iter().count());
for (key1, key2, reward) in v0::RelayerRewards::<T, I, LaneId>::drain() {
rewards_to_migrate.push((key1, key2, reward));
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
}
for (key1, key2, reward) in rewards_to_migrate {
let v0::RewardsAccountParams { owner, lane_id, bridged_chain_id } = key2;
register_relayer_reward_for_v1::<T, I, LaneId>(
v1::RewardsAccountParams::new(lane_id, bridged_chain_id, owner),
&key1,
reward,
);
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
}
weight
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
use codec::Encode;
use frame_support::BoundedBTreeMap;
use sp_runtime::traits::ConstU32;
let mut rewards: BoundedBTreeMap<
(T::AccountId, LaneId),
T::RewardBalance,
ConstU32<{ u32::MAX }>,
> = BoundedBTreeMap::new();
for (key1, key2, reward) in v0::RelayerRewards::<T, I, LaneId>::iter() {
log::info!(target: LOG_TARGET, "Reward to migrate: {key1:?}::{key2:?} - {reward:?}");
rewards = rewards
.try_mutate(|inner| {
inner
.entry((key1.clone(), key2.lane_id))
.and_modify(|value| *value += reward)
.or_insert(reward);
})
.unwrap();
}
log::info!(target: LOG_TARGET, "Found total rewards to migrate: {rewards:?}");
Ok(rewards.encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
use codec::Decode;
use frame_support::BoundedBTreeMap;
use sp_runtime::traits::ConstU32;
let rewards_before: BoundedBTreeMap<
(T::AccountId, LaneId),
T::RewardBalance,
ConstU32<{ u32::MAX }>,
> = Decode::decode(&mut &state[..]).unwrap();
let mut rewards_after: BoundedBTreeMap<
(T::AccountId, LaneId),
T::RewardBalance,
ConstU32<{ u32::MAX }>,
> = BoundedBTreeMap::new();
for (key1, key2, reward) in v1::RelayerRewards::<T, I, LaneId>::iter() {
log::info!(target: LOG_TARGET, "Migrated rewards: {key1:?}::{key2:?} - {reward:?}");
rewards_after = rewards_after
.try_mutate(|inner| {
inner
.entry((key1.clone(), *key2.lane_id()))
.and_modify(|value| *value += reward)
.or_insert(reward);
})
.unwrap();
}
log::info!(target: LOG_TARGET, "Found total migrated rewards: {rewards_after:?}");
frame_support::ensure!(
rewards_before == rewards_after,
"The rewards were not migrated correctly!."
);
log::info!(target: LOG_TARGET, "migrated all.");
Ok(())
}
}
pub type MigrationToV1<T, I, LaneId> = frame_support::migrations::VersionedMigration<
0,
1,
UncheckedMigrationV0ToV1<T, I, LaneId>,
Pallet<T, I>,
<T as frame_system::Config>::DbWeight,
>;
}
pub mod v2 {
use super::*;
#[cfg(feature = "try-runtime")]
use crate::RelayerRewards;
use crate::{Config, Pallet};
use bp_messages::LaneIdType;
use bp_relayers::RewardsAccountParams;
use core::marker::PhantomData;
use frame_support::traits::UncheckedOnRuntimeUpgrade;
pub struct UncheckedMigrationV1ToV2<T, I, LaneId>(PhantomData<(T, I, LaneId)>);
#[cfg(feature = "try-runtime")]
const LOG_TARGET: &str = "runtime::bridge-relayers-migration";
impl<T: Config<I>, I: 'static, LaneId: LaneIdType + Send + Sync> UncheckedOnRuntimeUpgrade
for UncheckedMigrationV1ToV2<T, I, LaneId>
where
<T as Config<I>>::Reward: From<RewardsAccountParams<LaneId>>,
{
fn on_runtime_upgrade() -> Weight {
let mut weight = T::DbWeight::get().reads(1);
let mut rewards_to_migrate =
Vec::with_capacity(v1::RelayerRewards::<T, I, LaneId>::iter().count());
for (key1, key2, reward) in v1::RelayerRewards::<T, I, LaneId>::drain() {
rewards_to_migrate.push((key1, key2, reward));
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
}
for (key1, key2, reward) in rewards_to_migrate {
let new_key2: T::Reward = key2.into();
Pallet::<T, I>::register_relayer_reward(new_key2, &key1, reward);
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
}
weight
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
use codec::Encode;
use frame_support::BoundedBTreeMap;
use sp_runtime::traits::ConstU32;
let mut rewards: BoundedBTreeMap<
(T::AccountId, Vec<u8>),
T::RewardBalance,
ConstU32<{ u32::MAX }>,
> = BoundedBTreeMap::new();
for (key1, key2, reward) in v1::RelayerRewards::<T, I, LaneId>::iter() {
let new_key2: T::Reward = key2.into();
log::info!(target: LOG_TARGET, "Reward to migrate: {key1:?}::{key2:?}->{new_key2:?} - {reward:?}");
rewards = rewards
.try_mutate(|inner| {
inner
.entry((key1.clone(), new_key2.encode()))
.and_modify(|value| *value += reward)
.or_insert(reward);
})
.unwrap();
}
log::info!(target: LOG_TARGET, "Found total rewards to migrate: {rewards:?}");
Ok(rewards.encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
use codec::{Decode, Encode};
use frame_support::BoundedBTreeMap;
use sp_runtime::traits::ConstU32;
let rewards_before: BoundedBTreeMap<
(T::AccountId, Vec<u8>),
T::RewardBalance,
ConstU32<{ u32::MAX }>,
> = Decode::decode(&mut &state[..]).unwrap();
let mut rewards_after: BoundedBTreeMap<
(T::AccountId, Vec<u8>),
T::RewardBalance,
ConstU32<{ u32::MAX }>,
> = BoundedBTreeMap::new();
for (key1, key2, reward) in v2::RelayerRewards::<T, I>::iter() {
log::info!(target: LOG_TARGET, "Migrated rewards: {key1:?}::{key2:?} - {reward:?}");
rewards_after = rewards_after
.try_mutate(|inner| {
inner
.entry((key1.clone(), key2.encode()))
.and_modify(|value| *value += reward)
.or_insert(reward);
})
.unwrap();
}
log::info!(target: LOG_TARGET, "Found total migrated rewards: {rewards_after:?}");
frame_support::ensure!(
rewards_before == rewards_after,
"The rewards were not migrated correctly!."
);
log::info!(target: LOG_TARGET, "migrated all.");
Ok(())
}
}
pub type MigrationToV2<T, I, LaneId> = frame_support::migrations::VersionedMigration<
1,
2,
UncheckedMigrationV1ToV2<T, I, LaneId>,
Pallet<T, I>,
<T as frame_system::Config>::DbWeight,
>;
}