diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index a837c1ae13..7689d763bb 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -49,6 +49,9 @@ use super::mock::*; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn account_migration_works() { + std::env::set_var("SNAP_RC", "/Users/vados/Documents/work/runtimes/polkadot.snap"); + std::env::set_var("SNAP_AH", "/Users/vados/Documents/work/runtimes/ah-polkadot.snap"); + std::env::set_var("START_STAGE", "crowdloan"); let Some((mut rc, mut ah)) = load_externalities().await else { return }; let para_id = ParaId::from(1000); diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index 60f35903a0..4d7c6a963e 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -17,13 +17,88 @@ use crate::*; use frame_support::traits::tokens::Preservation; -use polkadot_runtime_common::crowdloan as pallet_crowdloan; +use pallet_rc_migrator::crowdloan::RcCrowdloanMessage; impl Pallet { pub fn do_receive_crowdloan_messages( messages: Vec>, ) -> Result<(), Error> { - todo!() + let (mut good, mut bad) = (0, 0); + Self::deposit_event(Event::BatchReceived { + pallet: PalletEventName::Crowdloan, + count: messages.len() as u32, + }); + log::info!(target: LOG_TARGET, "Received {} crowdloan messages", messages.len()); + + for message in messages { + match Self::do_process_crowdloan_message(message) { + Ok(()) => good += 1, + Err(e) => { + bad += 1; + log::error!(target: LOG_TARGET, "Error while integrating crowdloan message: {:?}", e); + }, + } + } + + Self::deposit_event(Event::BatchProcessed { + pallet: PalletEventName::Crowdloan, + count_good: good, + count_bad: bad, + }); + + Ok(()) + } + + pub fn do_process_crowdloan_message(message: RcCrowdloanMessageOf) -> Result<(), Error> { + match message { + RcCrowdloanMessage::LeaseReserve { unreserve_block, account, para_id, amount } => { + log::info!(target: LOG_TARGET, "Integrating lease reserve for para_id: {:?}, account: {:?}, amount: {:?}, unreserve_block: {:?}", ¶_id, &account, &amount, &unreserve_block); + defensive_assert!(!RcLeaseReserve::::contains_key(( + unreserve_block, + &account, + para_id + ))); + + RcLeaseReserve::::insert((unreserve_block, account, para_id), amount); + }, + RcCrowdloanMessage::CrowdloanContribution { + withdraw_block, + contributor, + para_id, + amount, + crowdloan_account, + } => { + log::info!(target: LOG_TARGET, "Integrating crowdloan contribution for para_id: {:?}, contributor: {:?}, amount: {:?}, crowdloan_account: {:?}, withdraw_block: {:?}", ¶_id, &contributor, &amount, &crowdloan_account, &withdraw_block); + defensive_assert!(!RcCrowdloanContribution::::contains_key(( + withdraw_block, + &contributor, + para_id + ))); + + RcCrowdloanContribution::::insert( + (withdraw_block, contributor, para_id), + (crowdloan_account, amount), + ); + }, + RcCrowdloanMessage::CrowdloanDeposit { + unreserve_block, + para_id, + fund_index, + amount, + depositor, + } => { + log::info!(target: LOG_TARGET, "Integrating crowdloan deposit for para_id: {:?}, fund_index: {:?}, amount: {:?}, depositor: {:?}", ¶_id, &fund_index, &amount, &depositor); + defensive_assert!(!RcCrowdloanContribution::::contains_key(( + unreserve_block, + &depositor, + para_id + ))); + + RcCrowdloanReserve::::insert((unreserve_block, depositor, para_id), amount); + }, + } + + Ok(()) } } diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index dc17102e2c..0fbda4bd4d 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -50,7 +50,6 @@ pub mod vesting; pub use pallet::*; pub use pallet_rc_migrator::types::ZeroWeightOr; -use polkadot_runtime_common::crowdloan as pallet_crowdloan; use cumulus_primitives_core::ParaId; use frame_support::{ @@ -105,6 +104,7 @@ type RcAccountFor = RcAccount< pub enum PalletEventName { Indices, FastUnstake, + Crowdloan, BagsList, Vesting, Bounties, @@ -239,6 +239,26 @@ pub mod pallet { OptionQuery, >; + /// The reserve that was taken to create a crowdloan. + /// + /// This is normally 500 DOT and can be refunded as last step after all + /// `RcCrowdloanContribution`s of this loan have been withdrawn. + /// + /// Keys: + /// - Block number after which this can be unreserved + /// - + #[pallet::storage] + pub type RcCrowdloanReserve = StorageNMap< + _, + ( + NMapKey>, + NMapKey, + NMapKey, + ), + BalanceOf, + OptionQuery, + >; + #[pallet::error] pub enum Error { /// The error that should to be replaced by something meaningful. diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index 994ce3e3c9..a40c62a31d 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -27,18 +27,27 @@ pub struct CrowdloanMigrator { #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, Clone, PartialEq, Eq)] pub enum RcCrowdloanMessage { LeaseReserve { - block: BlockNumber, + /// The block number at which this deposit can be unreserved. + unreserve_block: BlockNumber, account: AccountId, para_id: ParaId, amount: Balance, }, CrowdloanContribution { - block: BlockNumber, + /// The block number at which this contribution can be withdrawn. + withdraw_block: BlockNumber, contributor: AccountId, para_id: ParaId, amount: Balance, crowdloan_account: AccountId, }, + CrowdloanDeposit { + unreserve_block: BlockNumber, + depositor: AccountId, + para_id: ParaId, + fund_index: u32, + amount: Balance, + }, } pub type RcCrowdloanMessageOf = @@ -48,11 +57,17 @@ pub type RcCrowdloanMessageOf = pub enum CrowdloanStage { LeaseReserve { last_key: Option }, CrowdloanContribution { last_key: Option }, + CrowdloanDeposit, Finished, } impl PalletMigration for CrowdloanMigrator -where crate::BalanceOf: From<<::Currency as frame_support::traits::Currency>::Balance> { + where + crate::BalanceOf: + From<<::Currency as frame_support::traits::Currency>::Balance>, + crate::BalanceOf: + From<<<::Auctioneer as polkadot_runtime_common::traits::Auctioneer<<<::Block as sp_runtime::traits::Block>::Header as sp_runtime::traits::Header>::Number>>::Currency as frame_support::traits::Currency>::Balance>, +{ type Key = CrowdloanStage; type Error = Error; @@ -68,11 +83,13 @@ where crate::BalanceOf: From<<:: .try_consume(::DbWeight::get().reads_writes(1, 1)) .is_err() { - if messages.is_empty() { + /*if messages.is_empty() { return Err(Error::OutOfWeight); } else { break; - } + }*/ + log::warn!("Out of weight, stop. num messages: {}", messages.len()); + break; } if messages.len() > 10_000 { @@ -89,39 +106,96 @@ where crate::BalanceOf: From<<:: match iter.next() { Some((para_id, leases)) => { - pallet_slots::Leases::::remove(¶_id); - let Some(last_lease) = leases.last() else { // This seems to be fine, but i don't know how it happens, see https://github.com/paritytech/polkadot-sdk/blob/db3ff60b5af2a9017cb968a4727835f3d00340f0/polkadot/runtime/common/src/slots/mod.rs#L108-L109 log::warn!(target: LOG_TARGET, "Empty leases for para_id: {:?}", para_id); + inner_key = CrowdloanStage::LeaseReserve { last_key: Some(para_id) }; continue; }; let Some((lease_acc, _)) = last_lease else { defensive!("Last lease cannot be None"); + inner_key = CrowdloanStage::LeaseReserve { last_key: Some(para_id) }; continue; }; // NOTE: Max instead of sum, see https://github.com/paritytech/polkadot-sdk/blob/db3ff60b5af2a9017cb968a4727835f3d00340f0/polkadot/runtime/common/src/slots/mod.rs#L102-L103 - let amount = leases.iter().flatten().map(|(_acc, amount)| amount).max().cloned().unwrap_or_default().into(); + let amount: crate::BalanceOf = leases.iter().flatten().map(|(_acc, amount)| amount).max().cloned().unwrap_or_default().into(); - if amount == 0 { - // fucking stupid ParaId type + if amount == 0u32.into() { defensive_assert!(para_id < ParaId::from(2000), "Must be system chain"); + inner_key = CrowdloanStage::LeaseReserve { last_key: Some(para_id) }; continue; } - let unlock_block = num_leases_to_ending_block::(leases.len() as u32); + let unreserve_block = num_leases_to_ending_block::(leases.len() as u32); - log::warn!(target: LOG_TARGET, "Migrating out lease reserve for para_id: {:?}, account: {:?}, amount: {:?}, unlock_block: {:?}", ¶_id, &lease_acc, &amount, &unlock_block); - messages.push(RcCrowdloanMessage::LeaseReserve { block: unlock_block, account: lease_acc.clone(), para_id, amount }); + log::warn!(target: LOG_TARGET, "Migrating out lease reserve for para_id: {:?}, account: {:?}, amount: {:?}, unreserve_block: {:?}", ¶_id, &lease_acc, &amount, &unreserve_block); + messages.push(RcCrowdloanMessage::LeaseReserve { unreserve_block, account: lease_acc.clone(), para_id, amount }); CrowdloanStage::LeaseReserve { last_key: Some(para_id) } }, None => CrowdloanStage::CrowdloanContribution { last_key: None }, } }, CrowdloanStage::CrowdloanContribution { last_key } => { - todo!() + let mut funds_iter = match last_key.clone() { + Some(last_key) => pallet_crowdloan::Funds::::iter_from_key(last_key), + None => pallet_crowdloan::Funds::::iter(), + }; + + let (para_id, fund) = match funds_iter.next() { + Some((para_id, fund)) => (para_id, fund), + None => { + inner_key = CrowdloanStage::CrowdloanDeposit; + continue; + }, + }; + + let mut contributions_iter = pallet_crowdloan::Pallet::::contribution_iterator(fund.fund_index); + + match contributions_iter.next() { + Some((contributor, (amount, memo))) => { + // Dont really care about memos, but we can add them, if needed. + if !memo.is_empty() { + log::warn!(target: LOG_TARGET, "Discarding crowdloan memo of length: {}", &memo.len()); + } + + let leases = pallet_slots::Leases::::get(para_id); + if leases.is_empty() { + defensive!("Leases should not be empty if there is a fund"); + } + + let crowdloan_account = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); + + let withdraw_block = num_leases_to_ending_block::(leases.len() as u32); + log::warn!(target: LOG_TARGET, "Migrating out crowdloan contribution for para_id: {:?}, contributor: {:?}, amount: {:?}, withdraw_block: {:?}", ¶_id, &contributor, &amount, &withdraw_block); + pallet_crowdloan::Pallet::::contribution_kill(fund.fund_index, &contributor); + messages.push(RcCrowdloanMessage::CrowdloanContribution { withdraw_block, contributor, para_id, amount: amount.into(), crowdloan_account }); + + inner_key // does not change since we deleted the contribution + }, + None => { + CrowdloanStage::CrowdloanContribution { last_key: Some(para_id) } + }, + } + }, + CrowdloanStage::CrowdloanDeposit => { + match pallet_crowdloan::Funds::::iter().next() { + Some((para_id, fund)) => { + pallet_crowdloan::Funds::::take(para_id); + + let leases = pallet_slots::Leases::::get(para_id); + if leases.is_empty() { + defensive!("Leases should not be empty if there is a fund"); + } + let unreserve_block = num_leases_to_ending_block::(leases.len() as u32); + + log::warn!(target: LOG_TARGET, "Migrating out crowdloan deposit for para_id: {:?}, fund_index: {:?}, amount: {:?}, depositor: {:?}", ¶_id, &fund.fund_index, &fund.deposit, &fund.depositor); + messages.push(RcCrowdloanMessage::CrowdloanDeposit { unreserve_block, para_id, fund_index: fund.fund_index, amount: fund.deposit.into(), depositor: fund.depositor }); + CrowdloanStage::CrowdloanDeposit + }, + None => CrowdloanStage::Finished, + } }, CrowdloanStage::Finished => { break; diff --git a/pallets/rc-migrator/src/lib.rs b/pallets/rc-migrator/src/lib.rs index 6b1dc4d2d3..e406454764 100644 --- a/pallets/rc-migrator/src/lib.rs +++ b/pallets/rc-migrator/src/lib.rs @@ -413,7 +413,12 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet - where crate::BalanceOf: From<<::Currency as frame_support::traits::Currency>::Balance>{ + where + crate::BalanceOf: + From<<::Currency as frame_support::traits::Currency>::Balance>, + crate::BalanceOf: + From<<<::Auctioneer as polkadot_runtime_common::traits::Auctioneer<<<::Block as sp_runtime::traits::Block>::Header as sp_runtime::traits::Header>::Number>>::Currency as frame_support::traits::Currency>::Balance>, + { fn on_initialize(_: BlockNumberFor) -> Weight { let mut weight_counter = WeightMeter::with_limit(T::MaxRcWeight::get()); let stage = RcMigrationStage::::get();