Skip to content
4 changes: 4 additions & 0 deletions integration-tests/ahm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ type RcChecks = (
pallet_rc_migrator::preimage::PreimageRequestStatusMigrator<Polkadot>,
pallet_rc_migrator::preimage::PreimageLegacyRequestStatusMigrator<Polkadot>,
pallet_rc_migrator::indices::IndicesMigrator<Polkadot>,
pallet_rc_migrator::claims::ClaimsMigrator<Polkadot>,
pallet_rc_migrator::crowdloan::CrowdloanMigrator<Polkadot>,
pallet_rc_migrator::vesting::VestingMigrator<Polkadot>,
pallet_rc_migrator::proxy::ProxyProxiesMigrator<Polkadot>,
pallet_rc_migrator::staking::bags_list::BagsListMigrator<Polkadot>,
Expand Down Expand Up @@ -90,6 +92,8 @@ type AhChecks = (
pallet_rc_migrator::preimage::PreimageRequestStatusMigrator<AssetHub>,
pallet_rc_migrator::preimage::PreimageLegacyRequestStatusMigrator<AssetHub>,
pallet_rc_migrator::indices::IndicesMigrator<AssetHub>,
pallet_rc_migrator::claims::ClaimsMigrator<AssetHub>,
pallet_rc_migrator::crowdloan::CrowdloanMigrator<AssetHub>,
pallet_rc_migrator::vesting::VestingMigrator<AssetHub>,
pallet_rc_migrator::proxy::ProxyProxiesMigrator<AssetHub>,
pallet_rc_migrator::staking::bags_list::BagsListMigrator<AssetHub>,
Expand Down
61 changes: 60 additions & 1 deletion pallets/ah-migrator/src/claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// limitations under the License.

use crate::*;
use pallet_rc_migrator::claims::{alias, RcClaimsMessage, RcClaimsMessageOf};
use pallet_rc_migrator::claims::{alias, ClaimsMigrator, RcClaimsMessage, RcClaimsMessageOf};

impl<T: Config> Pallet<T> {
pub fn do_receive_claims(messages: Vec<RcClaimsMessageOf<T>>) -> Result<(), Error<T>> {
Expand Down Expand Up @@ -86,3 +86,62 @@ impl<T: Config> Pallet<T> {
Ok(())
}
}

#[cfg(feature = "std")]
impl<T: Config> crate::types::AhMigrationCheck for ClaimsMigrator<T> {
type RcPrePayload = Vec<RcClaimsMessageOf<T>>;
type AhPrePayload = ();

fn pre_check(_: Self::RcPrePayload) -> Self::AhPrePayload {
// Ensure that the claims storage is empty before migration starts
assert!(
!pallet_claims::Total::<T>::exists(),
"Claims total should be empty before migration starts"
);
assert!(
alias::Claims::<T>::iter().next().is_none(),
"Claims should be empty before migration starts"
);
assert!(
alias::Vesting::<T>::iter().next().is_none(),
"Vesting should be empty before migration starts"
);
assert!(
alias::Signing::<T>::iter().next().is_none(),
"Signing should be empty before migration starts"
);
assert!(
alias::Preclaims::<T>::iter().next().is_none(),
"Preclaims should be empty before migration starts"
);
}

fn post_check(rc_pre_payload: Self::RcPrePayload, _: Self::AhPrePayload) {
let mut ah_messages = Vec::new();

// Collect current state
let total = pallet_claims::Total::<T>::get();
ah_messages.push(RcClaimsMessage::StorageValues { total });

for (address, amount) in alias::Claims::<T>::iter() {
ah_messages.push(RcClaimsMessage::Claims((address, amount)));
}

for (address, schedule) in alias::Vesting::<T>::iter() {
ah_messages.push(RcClaimsMessage::Vesting { who: address, schedule });
}

for (address, statement) in alias::Signing::<T>::iter() {
ah_messages.push(RcClaimsMessage::Signing((address, statement)));
}

for (account_id, address) in alias::Preclaims::<T>::iter() {
ah_messages.push(RcClaimsMessage::Preclaims((account_id, address)));
}

assert_eq!(
rc_pre_payload, ah_messages,
"Claims data mismatch: Asset Hub schedules differ from original Relay Chain schedules"
);
}
}
131 changes: 130 additions & 1 deletion pallets/ah-migrator/src/crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@

use crate::*;
use chrono::TimeZone;
use pallet_rc_migrator::crowdloan::RcCrowdloanMessage;
use cumulus_primitives_core::ParaId;
use pallet_rc_migrator::{
crowdloan::{CrowdloanMigrator, PreCheckMessage, RcCrowdloanMessage},
types::AccountIdOf,
};

impl<T: Config> Pallet<T> {
pub fn do_receive_crowdloan_messages(
Expand Down Expand Up @@ -169,3 +173,128 @@ where
block_timestamp
}
}

#[cfg(feature = "std")]
impl<T: Config> crate::types::AhMigrationCheck for CrowdloanMigrator<T> {
type RcPrePayload =
Vec<PreCheckMessage<BlockNumberFor<T>, AccountIdOf<T>, crate::BalanceOf<T>>>;
type AhPrePayload = ();

fn pre_check(_: Self::RcPrePayload) -> Self::AhPrePayload {
let crowdloan_data: Vec<_> = pallet_ah_ops::RcCrowdloanContribution::<T>::iter().collect();
assert!(
crowdloan_data.is_empty(),
"Crowdloan data should be empty before migration starts"
);
}

fn post_check(rc_pre_payload: Self::RcPrePayload, _: Self::AhPrePayload) {
use std::collections::BTreeMap;

// Helper function to verify that the reserves data matches between pre and post migration
// Takes:
// - reserves_pre: Reference to pre-migration reserves map
// - storage_iter: Iterator over storage items
// - error_msg: Custom error message for assertion failure
fn verify_reserves<T: Config, I>(
reserves_pre: &BTreeMap<ParaId, Vec<(BlockNumberFor<T>, AccountIdOf<T>, BalanceOf<T>)>>,
storage_iter: I,
error_msg: &str,
) where
I: Iterator<Item = ((BlockNumberFor<T>, ParaId, AccountIdOf<T>), BalanceOf<T>)>,
{
let mut reserves_post: BTreeMap<
ParaId,
Vec<(BlockNumberFor<T>, AccountIdOf<T>, BalanceOf<T>)>,
> = BTreeMap::new();
for ((block_number, para_id, account), amount) in storage_iter {
reserves_post.entry(para_id).or_insert_with(Vec::new).push((
block_number,
account,
amount,
));
}
assert_eq!(reserves_pre, &reserves_post, "{}", error_msg);
}

let mut rc_contributions: BTreeMap<
(ParaId, BlockNumberFor<T>, AccountIdOf<T>),
BalanceOf<T>,
> = BTreeMap::new();
let mut rc_lease_reserves: BTreeMap<
ParaId,
Vec<(BlockNumberFor<T>, AccountIdOf<T>, BalanceOf<T>)>,
> = BTreeMap::new();
let mut rc_crowdloan_reserves: BTreeMap<
ParaId,
Vec<(BlockNumberFor<T>, AccountIdOf<T>, BalanceOf<T>)>,
> = BTreeMap::new();

for message in rc_pre_payload {
match message {
PreCheckMessage::CrowdloanContribution {
withdraw_block,
contributor,
para_id,
amount,
..
} => {
rc_contributions
.entry((para_id, withdraw_block, contributor))
.and_modify(|e| *e = e.saturating_add(amount))
.or_insert(amount);
},
PreCheckMessage::LeaseReserve { unreserve_block, account, para_id, amount } => {
rc_lease_reserves.entry(para_id).or_insert_with(Vec::new).push((
unreserve_block,
account,
amount,
));
},
PreCheckMessage::CrowdloanReserve {
unreserve_block,
depositor,
para_id,
amount,
} => {
rc_crowdloan_reserves.entry(para_id).or_insert_with(Vec::new).push((
unreserve_block,
depositor,
amount,
));
},
}
}

// Verify contributions
let mut contributions_post: BTreeMap<
(ParaId, BlockNumberFor<T>, AccountIdOf<T>),
BalanceOf<T>,
> = BTreeMap::new();
for ((withdraw_block, para_id, contributor), (_, amount)) in
pallet_ah_ops::RcCrowdloanContribution::<T>::iter()
{
contributions_post
.entry((para_id, withdraw_block, contributor))
.and_modify(|e| *e = e.saturating_add(amount))
.or_insert(amount);
}

// Verify lease reserves
verify_reserves::<T, _>(
&rc_lease_reserves,
pallet_ah_ops::RcLeaseReserve::<T>::iter(),
"Lease reserve data mismatch: Asset Hub data differs from original Relay Chain data",
);

// Verify crowdloan reserves
verify_reserves::<T, _>(
&rc_crowdloan_reserves,
pallet_ah_ops::RcCrowdloanReserve::<T>::iter(),
"Crowdloan reserve data mismatch: Asset Hub data differs from original Relay Chain data",
);

// Verify contributions
assert_eq!(&rc_contributions, &contributions_post, "Crowdloan contribution data mismatch: Asset Hub data differs from original Relay Chain data");
}
}
60 changes: 60 additions & 0 deletions pallets/rc-migrator/src/claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,63 @@ pub mod alias {
(BalanceOf<T>, BalanceOf<T>, BlockNumberFor<T>),
>;
}

#[cfg(feature = "std")]
impl<T: Config> crate::types::RcMigrationCheck for ClaimsMigrator<T> {
type RcPrePayload = Vec<RcClaimsMessageOf<T>>;

fn pre_check() -> Self::RcPrePayload {
let mut messages = Vec::new();

// Collect StorageValues
let total = pallet_claims::Total::<T>::get();
messages.push(RcClaimsMessage::StorageValues { total });

// Collect Claims
for (address, amount) in alias::Claims::<T>::iter() {
messages.push(RcClaimsMessage::Claims((address, amount)));
}

// Collect Vesting
for (address, schedule) in alias::Vesting::<T>::iter() {
messages.push(RcClaimsMessage::Vesting { who: address, schedule });
}

// Collect Signing
for (address, statement) in alias::Signing::<T>::iter() {
messages.push(RcClaimsMessage::Signing((address, statement)));
}

// Collect Preclaims
for (account_id, address) in alias::Preclaims::<T>::iter() {
messages.push(RcClaimsMessage::Preclaims((account_id, address)));
}

messages
}

fn post_check(_: Self::RcPrePayload) {
assert!(
!pallet_claims::Total::<T>::exists(),
"Claims total should be empty after migration"
);
assert!(
alias::Claims::<T>::iter().next().is_none(),
"Claims should be empty after migration"
);
assert!(
alias::Vesting::<T>::iter().next().is_none(),
"Vesting should be empty after migration"
);
assert!(
alias::Signing::<T>::iter().next().is_none(),
"Signing should be empty after migration"
);
assert!(
alias::Preclaims::<T>::iter().next().is_none(),
"Preclaims should be empty after migration"
);

log::info!("All claims data successfully migrated and cleared from the Relay Chain.");
}
}
Loading
Loading