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 @@ -61,6 +61,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 All @@ -75,6 +77,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"
);
}
}
83 changes: 82 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, RcCrowdloanMessage},
types::AccountIdOf,
};

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

#[cfg(feature = "std")]
impl<T: Config> crate::types::AhMigrationCheck for CrowdloanMigrator<T> {
/// Pre-migration payload for crowdloan data:
/// - `ParaId`: The parachain identifier
/// - Inner Vec contains contributions, where each tuple is:
/// - `BlockNumberFor<T>`: The block number at which this deposit can be unreserved
/// - `AccountId`: The depositor account
/// - `BalanceOf<T>`: The reserved amount
type RcPrePayload = Vec<(ParaId, Vec<(BlockNumberFor<T>, AccountIdOf<T>, 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<(BlockNumberFor<T>, ParaId, AccountIdOf<T>), BalanceOf<T>>,
storage_iter: I,
error_msg: &str,
) where
I: Iterator<Item = ((BlockNumberFor<T>, ParaId, AccountIdOf<T>), BalanceOf<T>)>,
{
let reserves_post: BTreeMap<_, _> = storage_iter.collect();
assert_eq!(reserves_pre, &reserves_post, "{}", error_msg);
}

let rc_pre: BTreeMap<
ParaId,
Vec<(BlockNumberFor<T>, <T as frame_system::Config>::AccountId, BalanceOf<T>)>,
> = rc_pre_payload.iter().cloned().collect();

let all_post: BTreeMap<_, _> = pallet_ah_ops::RcCrowdloanContribution::<T>::iter()
.map(|((block_number, para_id, contributor), (_, amount))| {
(para_id, vec![(block_number, contributor, amount)])
})
.collect();

assert_eq!(
rc_pre, all_post,
"Crowdloan data mismatch: Asset Hub data differs from original Relay Chain data"
);

// Transform pre-migration payload into a format suitable for reserves comparison
let reserves_pre: BTreeMap<_, _> = rc_pre_payload
.iter()
.flat_map(|(para_id, entries)| {
entries.iter().map(move |(block_number, account_id, amount)| {
((block_number.clone(), para_id.clone(), account_id.clone()), *amount)
})
})
.collect();

verify_reserves::<T, _>(
&reserves_pre,
pallet_ah_ops::RcLeaseReserve::<T>::iter(),
"Lease reserve data mismatch: Asset Hub data differs from original Relay Chain data",
);
verify_reserves::<T, _>(
&reserves_pre,
pallet_ah_ops::RcCrowdloanReserve::<T>::iter(),
"Crowdloan reserve 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.");
}
}
70 changes: 70 additions & 0 deletions pallets/rc-migrator/src/crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,73 @@ pub fn num_leases_to_ending_block<T: Config>(num_leases: u32) -> Result<BlockNum
.ok_or(())?;
Ok(last_period_end_block)
}

#[cfg(feature = "std")]
impl<T: Config> crate::types::RcMigrationCheck for CrowdloanMigrator<T> {
/// Pre-migration payload for crowdloan data:
/// - `ParaId`: The parachain identifier
/// - Inner Vec contains contributions, where each tuple is:
/// - `BlockNumberFor<T>`: The block number at which this deposit can be unreserved
/// - `AccountId`: The depositor account
/// - `BalanceOf<T>`: The reserved amount
type RcPrePayload = Vec<(ParaId, Vec<(BlockNumberFor<T>, AccountIdOf<T>, BalanceOf<T>)>)>;

fn pre_check() -> Self::RcPrePayload {
// Iterate through all crowdloan funds and collect their contributions
pallet_crowdloan::Funds::<T>::iter()
.map(|(para_id, fund)| {
// For each fund, collect all contributions into a vector
let contributions =
pallet_crowdloan::Pallet::<T>::contribution_iterator(fund.fund_index)
.map(|(contributor, (amount, encoded_block_number))| {
// Decode the block number from bytes, defaulting to 0 if decoding fails
let block_number =
BlockNumberFor::<T>::decode(&mut &encoded_block_number[..])
.unwrap_or_default();

// Create a tuple of (block_number, contributor, amount)
// Convert amount to the expected balance type, defaulting to 0 if
// conversion fails
(block_number, contributor, amount.try_into().unwrap_or_default())
})
.collect();

// Return tuple of parachain ID and its contributions
(para_id, contributions)
})
.collect()
}

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

// Collect current state of crowdloan funds and their contributions
let current_map: BTreeMap<ParaId, Vec<(BlockNumberFor<T>, AccountIdOf<T>, BalanceOf<T>)>> =
pallet_crowdloan::Funds::<T>::iter()
.map(|(para_id, fund)| {
// For each fund, collect all its contributions
let contributions =
pallet_crowdloan::Pallet::<T>::contribution_iterator(fund.fund_index)
.map(|(contributor, (amount, encoded_block_number))| {
// Decode the block number from bytes
let block_number =
BlockNumberFor::<T>::decode(&mut &encoded_block_number[..])
.unwrap_or_default();

// Create a tuple of (block_number, contributor, amount)
(block_number, contributor, amount.try_into().unwrap_or_default())
})
.collect();

(para_id, contributions)
})
.collect();

// Verify that all data has been properly migrated
assert!(current_map.is_empty(), "Current crowdloan data should be empty after migration");

// Double check that no funds remain
let funds_empty = pallet_crowdloan::Funds::<T>::iter().next().is_none();
assert!(funds_empty, "pallet_crowdloan::Funds should be empty after migration");
}
}
Loading