From 3824a4dbb59937bcb61fedd2be5593ff84d25977 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 7 Feb 2025 17:07:37 +0100 Subject: [PATCH 01/22] wip Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 1 + integration-tests/ahm/Cargo.toml | 1 + integration-tests/ahm/src/tests.rs | 35 ++++++++++++++++++++++++++++ pallets/rc-migrator/src/crowdloan.rs | 29 +++++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 pallets/rc-migrator/src/crowdloan.rs diff --git a/Cargo.lock b/Cargo.lock index e8502dbbeb..99d7ef948a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10077,6 +10077,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-primitives 16.0.0", "polkadot-runtime", + "polkadot-runtime-common", "polkadot-runtime-constants", "polkadot-runtime-parachains", "sc-consensus-grandpa", diff --git a/integration-tests/ahm/Cargo.toml b/integration-tests/ahm/Cargo.toml index c69ea5c468..f95d0bf1f4 100644 --- a/integration-tests/ahm/Cargo.toml +++ b/integration-tests/ahm/Cargo.toml @@ -26,6 +26,7 @@ parachains-common = { workspace = true, default-features = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-runtime = { workspace = true } +polkadot-runtime-common = { workspace = true, default-features = true } polkadot-runtime-constants = { workspace = true, default-features = true } remote-externalities = { workspace = true } runtime-parachains = { workspace = true, default-features = true } diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index 3831ea1e93..a6cff91d5b 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -35,7 +35,10 @@ use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::traits::*; use pallet_rc_migrator::{types::PalletMigrationChecks, MigrationStage, RcMigrationStage}; use std::str::FromStr; +use polkadot_runtime_common::crowdloan as pallet_crowdloan; +use polkadot_runtime_common::paras_registrar; +use polkadot_runtime::Block as PolkadotBlock; use asset_hub_polkadot_runtime::Runtime as AssetHub; use polkadot_runtime::Runtime as Polkadot; @@ -113,3 +116,35 @@ async fn account_migration_works() { // some overweight ones. }); } + +/// Check that our function to calculate the unlock time of a crowdloan contribution is correct. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn crowdloan_unlock_times_are_correct_works() { + let mut rc = remote_ext_test_setup::("SNAP_RC").await.unwrap() else { return }; + let para_id = ParaId::from(1000); + let mut wasted = 0; + + rc.execute_with(|| { + for para in paras_registrar::Paras::::iter_keys() { + let id: u32 = para.into(); + let acala_fund_id = pallet_crowdloan::Pallet::::fund_account_id(id); + + let acc = frame_system::Account::::get(&acala_fund_id).data; + if acc.free > 0 && acc.reserved == 0 { + println!("Para: {}, Fund id: {} with {} free", id, acala_fund_id, acc.free); + wasted += acc.free; + } + } + + println!("Wasted: {}", wasted); + }); +} + +// The block after which a crowdloan contribution will be able to redeem their contribution. +/*fn crowdloan_unlock_block(para_id: ParaId) -> u64 { + let lease_period = T::LeasePeriod::get(); + let fund_index = T::FundIndex::get(); + let fund_period = fund_index / lease_period; + lease_period * fund_period + fund_index +} +*/ diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs new file mode 100644 index 0000000000..5c5dbf20f4 --- /dev/null +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -0,0 +1,29 @@ +// notes: +// - deposit from the funds map need to be unreserved to the parachain manager https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L416-L417 +// - when refunding crowdloan contributions, we need to re-activate the issuance https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L792-L793 +// - burn remaining funds in the crowdloan account https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L573-L574 + +/* + +Crowdloan 2008 + +{ + depositor: 12xWvNmBVt5541brRVANFaFejMrttu8tnBso3NgzsSuZnY7f + verifier: null + deposit: 5,000,000,000,000 + raised: 2,220,000,000,000 + end: 21,413,000 + cap: 500,000,000,000,000 + lastContribution: { + Ending: 21,055,050 + } + firstPeriod: 17 + lastPeriod: 24 + fundIndex: 91 +} + +https://polkadot.subscan.io/crowdloan/2008-44 + +Ended + +*/ From 7e58826f3f02ced05c355392eb1507c8a8ef9025 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 8 Feb 2025 16:47:53 +0100 Subject: [PATCH 02/22] tests Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 9 +- Cargo.toml | 1 + integration-tests/ahm/Cargo.toml | 3 + integration-tests/ahm/src/tests.rs | 132 ++++++++++++++++++++++++++--- 4 files changed, 130 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99d7ef948a..83d1fcd046 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2281,9 +2281,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -2291,7 +2291,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -10063,6 +10063,7 @@ name = "polkadot-integration-tests-ahm" version = "1.0.0" dependencies = [ "asset-hub-polkadot-runtime", + "chrono", "cumulus-primitives-core", "emulated-integration-tests-common", "frame-remote-externalities", @@ -10070,9 +10071,11 @@ dependencies = [ "frame-system", "log", "pallet-ah-migrator", + "pallet-balances", "pallet-indices", "pallet-message-queue", "pallet-rc-migrator", + "pallet-timestamp", "parachains-common", "polkadot-parachain-primitives", "polkadot-primitives 16.0.0", diff --git a/Cargo.toml b/Cargo.toml index 764768758c..f9258412c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ pallet-rc-migrator = { path = "pallets/rc-migrator", default-features = false } pallet-ah-migrator = { path = "pallets/ah-migrator", default-features = false } assert_matches = { version = "1.5.0" } approx = { version = "0.5.1" } +chrono = { version = "0.4.39", default-features = false } asset-hub-kusama-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-kusama" } asset-hub-kusama-runtime = { path = "system-parachains/asset-hubs/asset-hub-kusama" } asset-hub-polkadot-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-polkadot" } diff --git a/integration-tests/ahm/Cargo.toml b/integration-tests/ahm/Cargo.toml index f95d0bf1f4..054efbedea 100644 --- a/integration-tests/ahm/Cargo.toml +++ b/integration-tests/ahm/Cargo.toml @@ -13,6 +13,7 @@ authority-discovery-primitives = { workspace = true, default-features = true } babe-primitives = { workspace = true, default-features = true } beefy-primitives = { workspace = true, default-features = true } cumulus-primitives-core = { workspace = true, default-features = true } +chrono = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } frame-support = { workspace = true, default-features = true } frame-system = { workspace = true, default-features = true } @@ -20,8 +21,10 @@ grandpa = { workspace = true } log = { workspace = true, default-features = true } pallet-rc-migrator = { workspace = true, default-features = true } pallet-ah-migrator = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } pallet-indices = { workspace = true, default-features = true } pallet-message-queue = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } parachains-common = { workspace = true, default-features = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index a6cff91d5b..545cefa7c9 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -35,9 +35,11 @@ use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::traits::*; use pallet_rc_migrator::{types::PalletMigrationChecks, MigrationStage, RcMigrationStage}; use std::str::FromStr; +use std::io::Write; use polkadot_runtime_common::crowdloan as pallet_crowdloan; use polkadot_runtime_common::paras_registrar; +use polkadot_runtime_common::slots as pallet_slots; use polkadot_runtime::Block as PolkadotBlock; use asset_hub_polkadot_runtime::Runtime as AssetHub; use polkadot_runtime::Runtime as Polkadot; @@ -117,26 +119,132 @@ async fn account_migration_works() { }); } +use sp_runtime::AccountId32; +use sp_runtime::traits::Dispatchable; + /// Check that our function to calculate the unlock time of a crowdloan contribution is correct. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn crowdloan_unlock_times_are_correct_works() { - let mut rc = remote_ext_test_setup::("SNAP_RC").await.unwrap() else { return }; - let para_id = ParaId::from(1000); - let mut wasted = 0; + let mut rc = remote_ext_test_setup::("SNAP_RC").await.unwrap(); rc.execute_with(|| { - for para in paras_registrar::Paras::::iter_keys() { - let id: u32 = para.into(); - let acala_fund_id = pallet_crowdloan::Pallet::::fund_account_id(id); - - let acc = frame_system::Account::::get(&acala_fund_id).data; - if acc.free > 0 && acc.reserved == 0 { - println!("Para: {}, Fund id: {} with {} free", id, acala_fund_id, acc.free); - wasted += acc.free; + let now = frame_system::Pallet::::block_number(); + let mut para_ids = pallet_crowdloan::Funds::::iter_keys().collect::>(); + para_ids.sort(); + + for para_id in para_ids.clone() { + let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); + // they all ended + assert!(fund.end < now); + + let id: u32 = para_id.into(); + let fund_id = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); + + let acc = frame_system::Account::::get(&fund_id); + let total = acc.data.free + acc.data.reserved; + + if acc.data.reserved == 0 { + println!("$ Para: {}, Fund index: {}, id: {} Can be dissolved", para_id, fund.fund_index, fund_id); + + assert!(fund.raised == 0); + assert!(fund.deposit != 0, "we must have a deposit"); // TODO refund + ensure_can_dissolve(para_id); + + continue; + } + + println!(" Para: {}, Fund index: {}, id: {}, with {} total", para_id, fund.fund_index, fund_id, total / 10_000_000_000); + } + + println!("#### Done ####"); + let mut refunds: String = "para_id,fund_id,account,amount,refund_date\n".into(); + + for para_id in para_ids { + let Some(fund) = pallet_crowdloan::Funds::::get(para_id) else { + continue; + }; + + let id: u32 = para_id.into(); + let fund_id = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); + + let acc = frame_system::Account::::get(&fund_id); + let total = acc.data.free + acc.data.reserved; + let refund_time = block_number_to_date(calculate_refund_time(para_id)); + + let mut contrib_iter = pallet_crowdloan::Pallet::::contribution_iterator(fund.fund_index); + let mut total_contrib = 0; + let mut num_contribs = 0; + + while let Some((contributor, (contrib, _memo))) = contrib_iter.next() { + total_contrib += contrib; + num_contribs += 1; + let amount = format!("{}", contrib / 10_000_000_000).replace(",", "_"); + refunds.push_str(&format!("{},{},{},{},{}\n", para_id, fund.fund_index, contributor, amount, refund_time)); + } + assert_eq!(total_contrib, fund.raised); + + println!(" Para: {}, Fund index: {}, id: {}, with {} total, {} contribs", para_id, fund.fund_index, fund_id, total / 10_000_000_000, num_contribs); + if acc.data.free + acc.data.reserved > fund.raised { + println!("! Over funded by {}", (acc.data.free + acc.data.reserved - fund.raised) / 10_000_000_000); } } - println!("Wasted: {}", wasted); + // write to file + let mut file = std::fs::File::create("/Users/vados/Documents/work/runtimes/refunds.csv").unwrap(); + file.write_all(refunds.as_bytes()).unwrap(); + }); +} + +/// Calculate when a crowdloan will be able to dissolve. +fn calculate_refund_time(para_id: ParaId) -> u32 { + let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); + let dissolve_time = fund.last_period * ::LeasePeriod::get() + ::LeaseOffset::get(); + //ensure_can_refund(para_id, dissolve_time); + dissolve_time +} + +fn block_number_to_date(block_number: u32) -> String { + let block_now = frame_system::Pallet::::block_number(); + let unix_now = pallet_timestamp::Now::::get(); + let date = unix_now as i128 + (block_number as i128 - block_now as i128) as i128 * 6_000; + chrono::NaiveDateTime::from_timestamp_millis(date as i64).unwrap().to_string() +} + +fn ensure_can_refund(para_id: ParaId, at: u32) { + frame_support::hypothetically!({ + let alice = AccountId32::new([0u8; 32]); + pallet_balances::Pallet::::make_free_balance_be(&alice, 100_000_000_000_000_000); + run_to_block(at + 10); + + let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); + let origin = polkadot_runtime::RuntimeOrigin::signed(alice); + let call: polkadot_runtime::RuntimeCall = pallet_crowdloan::Call::::refund { index: para_id }.into(); + call.dispatch_bypass_filter(origin).unwrap(); // Why bypass? + }); +} + +fn run_to_block(at: u32) { + let mut bn = frame_system::Pallet::::block_number(); + + frame_system::Pallet::::set_block_number(bn); + polkadot_runtime::AllPalletsWithSystem::on_initialize(bn); +} + +fn ensure_can_dissolve(para_id: ParaId) { + frame_support::hypothetically!({ + let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); + let sender = fund.depositor; + println!("fund index: {}, sender: {}, deposit: {}", fund.fund_index, sender, fund.deposit); + let data_before = frame_system::Account::::get(&sender).data; + let origin = polkadot_runtime::RuntimeOrigin::signed(sender.clone()); + + let call: polkadot_runtime::RuntimeCall = pallet_crowdloan::Call::::dissolve { index: para_id }.into(); + call.dispatch(origin).unwrap(); + let data_after = frame_system::Account::::get(&sender).data; + + if data_after.reserved >= data_before.reserved || data_after.free <= data_before.free { + println!("! Could not unreserve"); + } }); } From c5361f818d538b66797fead7c682de2aa8cf8480 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 12 Feb 2025 23:16:39 +0100 Subject: [PATCH 03/22] try Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 3 + Cargo.toml | 2 +- integration-tests/ahm/Cargo.toml | 2 + integration-tests/ahm/src/tests.rs | 204 +++++++++++++++++++++------ pallets/ah-migrator/Cargo.toml | 1 + pallets/ah-migrator/src/crowdloan.rs | 84 +++++++++++ pallets/ah-migrator/src/lib.rs | 108 +++++++++++++- pallets/rc-migrator/src/crowdloan.rs | 176 +++++++++++++++++++---- pallets/rc-migrator/src/lib.rs | 55 +++++++- pallets/rc-migrator/src/types.rs | 2 + 10 files changed, 563 insertions(+), 74 deletions(-) create mode 100644 pallets/ah-migrator/src/crowdloan.rs diff --git a/Cargo.lock b/Cargo.lock index 900411e14f..543a1c36ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7646,6 +7646,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" name = "pallet-ah-migrator" version = "0.1.0" dependencies = [ + "cumulus-primitives-core", "frame-benchmarking", "frame-support", "frame-system", @@ -10078,6 +10079,7 @@ dependencies = [ "frame-remote-externalities", "frame-support", "frame-system", + "hex", "log", "pallet-ah-migrator", "pallet-balances", @@ -10086,6 +10088,7 @@ dependencies = [ "pallet-rc-migrator", "pallet-timestamp", "parachains-common", + "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-primitives 16.0.0", "polkadot-runtime", diff --git a/Cargo.toml b/Cargo.toml index f9258412c2..2f0ad52e96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -321,7 +321,7 @@ members = [ panic = "unwind" opt-level = 3 # AHM: FAIL-CI -debug-assertions = true +debug-assertions = false overflow-checks = true [profile.production] diff --git a/integration-tests/ahm/Cargo.toml b/integration-tests/ahm/Cargo.toml index 054efbedea..4df7775ede 100644 --- a/integration-tests/ahm/Cargo.toml +++ b/integration-tests/ahm/Cargo.toml @@ -8,6 +8,7 @@ description = "Polkadot integration tests for the Asset Hub Migration" publish = false [dev-dependencies] +codec = { workspace = true, default-features = true } asset-hub-polkadot-runtime = { workspace = true } authority-discovery-primitives = { workspace = true, default-features = true } babe-primitives = { workspace = true, default-features = true } @@ -18,6 +19,7 @@ emulated-integration-tests-common = { workspace = true } frame-support = { workspace = true, default-features = true } frame-system = { workspace = true, default-features = true } grandpa = { workspace = true } +hex = { workspace = true, default-features = true } log = { workspace = true, default-features = true } pallet-rc-migrator = { workspace = true, default-features = true } pallet-ah-migrator = { workspace = true, default-features = true } diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index 545cefa7c9..a837c1ae13 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -31,18 +31,19 @@ //! SNAP_RC="../../polkadot.snap" SNAP_AH="../../ah-polkadot.snap" RUST_LOG="info" ct polkadot-integration-tests-ahm -r on_initialize_works -- --nocapture //! ``` +use asset_hub_polkadot_runtime::Runtime as AssetHub; +use codec::Encode; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::traits::*; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_rc_migrator::{types::PalletMigrationChecks, MigrationStage, RcMigrationStage}; -use std::str::FromStr; -use std::io::Write; -use polkadot_runtime_common::crowdloan as pallet_crowdloan; -use polkadot_runtime_common::paras_registrar; - -use polkadot_runtime_common::slots as pallet_slots; -use polkadot_runtime::Block as PolkadotBlock; -use asset_hub_polkadot_runtime::Runtime as AssetHub; -use polkadot_runtime::Runtime as Polkadot; +use parachains_common::impls::BalanceOf; +use polkadot_runtime::{Block as PolkadotBlock, Runtime as Polkadot}; +use polkadot_runtime_common::{ + crowdloan as pallet_crowdloan, paras_registrar, paras_registrar as pallet_registrar, + slots as pallet_slots, +}; +use std::{collections::BTreeMap, io::Write, str::FromStr}; use super::mock::*; @@ -119,12 +120,14 @@ async fn account_migration_works() { }); } -use sp_runtime::AccountId32; -use sp_runtime::traits::Dispatchable; +use sp_runtime::{traits::Dispatchable, AccountId32}; /// Check that our function to calculate the unlock time of a crowdloan contribution is correct. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn crowdloan_unlock_times_are_correct_works() { + std::env::set_var("SNAP_RC", "/Users/vados/Documents/work/runtimes/polkadot.snap"); + std::env::set_var("START_STAGE", "preimage"); + let mut rc = remote_ext_test_setup::("SNAP_RC").await.unwrap(); rc.execute_with(|| { @@ -134,7 +137,6 @@ async fn crowdloan_unlock_times_are_correct_works() { for para_id in para_ids.clone() { let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); - // they all ended assert!(fund.end < now); let id: u32 = para_id.into(); @@ -143,35 +145,46 @@ async fn crowdloan_unlock_times_are_correct_works() { let acc = frame_system::Account::::get(&fund_id); let total = acc.data.free + acc.data.reserved; + // All crowdloans without reserved amount can be dissolved. We will do this as part of + // the migration if acc.data.reserved == 0 { - println!("$ Para: {}, Fund index: {}, id: {} Can be dissolved", para_id, fund.fund_index, fund_id); + println!( + "$ Para: {}, Fund index: {}, id: {} Can be dissolved", + para_id, fund.fund_index, fund_id + ); assert!(fund.raised == 0); assert!(fund.deposit != 0, "we must have a deposit"); // TODO refund ensure_can_dissolve(para_id); - + continue; } - - println!(" Para: {}, Fund index: {}, id: {}, with {} total", para_id, fund.fund_index, fund_id, total / 10_000_000_000); + + println!( + " Para: {}, Fund index: {}, id: {}, with {} total", + para_id, + fund.fund_index, + fund_id, + total / 10_000_000_000 + ); } println!("#### Done ####"); let mut refunds: String = "para_id,fund_id,account,amount,refund_date\n".into(); - for para_id in para_ids { - let Some(fund) = pallet_crowdloan::Funds::::get(para_id) else { - continue; - }; + /*for para_id in para_ids { + let Some(fund) = pallet_crowdloan::Funds::::get(para_id) else { + continue; + }; - let id: u32 = para_id.into(); - let fund_id = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); + let id: u32 = para_id.into(); + let fund_id = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); - let acc = frame_system::Account::::get(&fund_id); - let total = acc.data.free + acc.data.reserved; - let refund_time = block_number_to_date(calculate_refund_time(para_id)); + let acc = frame_system::Account::::get(&fund_id); + let total = acc.data.free + acc.data.reserved;*/ + let refund_time = calculate_refund_time(para_ids); - let mut contrib_iter = pallet_crowdloan::Pallet::::contribution_iterator(fund.fund_index); + /*let mut contrib_iter = pallet_crowdloan::Pallet::::contribution_iterator(fund.fund_index); let mut total_contrib = 0; let mut num_contribs = 0; @@ -187,26 +200,108 @@ async fn crowdloan_unlock_times_are_correct_works() { if acc.data.free + acc.data.reserved > fund.raised { println!("! Over funded by {}", (acc.data.free + acc.data.reserved - fund.raised) / 10_000_000_000); } - } + }*/ // write to file - let mut file = std::fs::File::create("/Users/vados/Documents/work/runtimes/refunds.csv").unwrap(); - file.write_all(refunds.as_bytes()).unwrap(); + //let mut file = + // std::fs::File::create("/Users/vados/Documents/work/runtimes/refunds.csv").unwrap(); + // file.write_all(refunds.as_bytes()).unwrap(); }); } /// Calculate when a crowdloan will be able to dissolve. -fn calculate_refund_time(para_id: ParaId) -> u32 { - let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); - let dissolve_time = fund.last_period * ::LeasePeriod::get() + ::LeaseOffset::get(); - //ensure_can_refund(para_id, dissolve_time); - dissolve_time +fn calculate_refund_time(mut para_ids: Vec) -> BTreeMap> { + let mut cutoff = 10_000_000; // some high number to make the test timeout if there is an error + let mut original_reserved: BTreeMap> = BTreeMap::new(); + let orig_len = para_ids.len(); + let mut refund_times: BTreeMap> = BTreeMap::new(); + + frame_support::hypothetically!({ + while !para_ids.is_empty() && cutoff > 0 { + let now = frame_system::Pallet::::block_number(); + + para_ids = para_ids + .into_iter() + .filter(|para_id| { + let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); + let slots = pallet_slots::Leases::::get(para_id); + // Lease pot account can either be a crowdloan account or a solo bidder + log::info!("[{now}] Para {} has {} slots", para_id, slots.len()); + let Some(last_lease) = slots.last().cloned() else { + // TODO check this + // TODO additional check with crowdloan account has no reserve + log::info!( + "[{now}] Para {} has no slots and already had its funds unreserved", + para_id + ); + // TODO https://polkadot.subsquare.io/referenda/524 + if *para_id == ParaId::from(3356) { + return false; + } + refund_times.insert(*para_id, now); + // TODO some additional checks i forgot about + // Account must have at least `rased` in free funds + let pot = + pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); + let pot_acc = frame_system::Account::::get(&pot); + if pot_acc.data.free < fund.raised { + panic!( + "Para {} has {} raised but only {} free", + para_id, fund.raised, pot_acc.data.free + ); + } + return false; + }; + + let Some((lease_pot_account, deposit_amount)) = last_lease else { + frame_support::defensive!("Last lease should never be None"); + return false; + }; + + let reserved = + pallet_balances::Pallet::::reserved_balance(&lease_pot_account); + let original_res = + original_reserved.entry(lease_pot_account).or_insert(reserved); + if reserved < *original_res { + log::info!( + "[{}] Lease funds of para {} can be withdrawn, reserved: {} -> {}", + now, + para_id, + *original_res, + reserved + ); + assert_eq(*original_res - reserved, deposit_amount); + assert_eq(fund.raised, 0); + // TODO additional checks if there is a crowdloan, then it should be zero + return false; + } + + true + }) + .collect(); + + // Go to the start of the next Lease period + let offset = ::LeaseOffset::get(); + let period = ::LeasePeriod::get(); + let next_period_start = ((now - offset) / period) * period + period + offset; + + run_to_block(next_period_start); + cutoff -= 1; + } + + if !para_ids.is_empty() { + panic!("Some crowdloans could not be dissolved: {:?}", para_ids); + } + }); + // TODO -1 for the Bifrost lease swap 3356 mentioned above + assert_eq!(orig_len - 1, refund_times.len()); + refund_times } fn block_number_to_date(block_number: u32) -> String { - let block_now = frame_system::Pallet::::block_number(); - let unix_now = pallet_timestamp::Now::::get(); - let date = unix_now as i128 + (block_number as i128 - block_now as i128) as i128 * 6_000; + let block_now = frame_system::Pallet::::block_number(); + let unix_now = pallet_timestamp::Now::::get(); + let date = unix_now as i128 + (block_number as i128 - block_now as i128) as i128 * 6_000; chrono::NaiveDateTime::from_timestamp_millis(date as i64).unwrap().to_string() } @@ -214,20 +309,33 @@ fn ensure_can_refund(para_id: ParaId, at: u32) { frame_support::hypothetically!({ let alice = AccountId32::new([0u8; 32]); pallet_balances::Pallet::::make_free_balance_be(&alice, 100_000_000_000_000_000); - run_to_block(at + 10); + run_to_block(at); let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); let origin = polkadot_runtime::RuntimeOrigin::signed(alice); - let call: polkadot_runtime::RuntimeCall = pallet_crowdloan::Call::::refund { index: para_id }.into(); + let call: polkadot_runtime::RuntimeCall = + pallet_crowdloan::Call::::refund { index: para_id }.into(); call.dispatch_bypass_filter(origin).unwrap(); // Why bypass? }); } fn run_to_block(at: u32) { - let mut bn = frame_system::Pallet::::block_number(); - - frame_system::Pallet::::set_block_number(bn); - polkadot_runtime::AllPalletsWithSystem::on_initialize(bn); + //let mut bn = frame_system::Pallet::::block_number(); + + frame_system::Pallet::::set_block_number(at); + //polkadot_runtime::AllPalletsWithSystem::on_initialize(bn); + as frame_support::traits::OnInitialize< + BlockNumberFor, + >>::on_initialize(at); + as frame_support::traits::OnInitialize< + BlockNumberFor, + >>::on_initialize(at); + as frame_support::traits::OnInitialize< + BlockNumberFor, + >>::on_initialize(at); + as frame_support::traits::OnFinalize< + BlockNumberFor, + >>::on_finalize(at); } fn ensure_can_dissolve(para_id: ParaId) { @@ -238,7 +346,8 @@ fn ensure_can_dissolve(para_id: ParaId) { let data_before = frame_system::Account::::get(&sender).data; let origin = polkadot_runtime::RuntimeOrigin::signed(sender.clone()); - let call: polkadot_runtime::RuntimeCall = pallet_crowdloan::Call::::dissolve { index: para_id }.into(); + let call: polkadot_runtime::RuntimeCall = + pallet_crowdloan::Call::::dissolve { index: para_id }.into(); call.dispatch(origin).unwrap(); let data_after = frame_system::Account::::get(&sender).data; @@ -256,3 +365,10 @@ fn ensure_can_dissolve(para_id: ParaId) { lease_period * fund_period + fund_index } */ + +/// Assert that also works without debug_assert +fn assert_eq(a: R, b: R) { + if a != b { + panic!("{a:?} != {b:?}"); + } +} diff --git a/pallets/ah-migrator/Cargo.toml b/pallets/ah-migrator/Cargo.toml index 93ada94d27..7810747066 100644 --- a/pallets/ah-migrator/Cargo.toml +++ b/pallets/ah-migrator/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true [dependencies] codec = { workspace = true, features = ["max-encoded-len"] } +cumulus-primitives-core = { workspace = true } frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs new file mode 100644 index 0000000000..60f35903a0 --- /dev/null +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -0,0 +1,84 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use frame_support::traits::tokens::Preservation; +use polkadot_runtime_common::crowdloan as pallet_crowdloan; + +impl Pallet { + pub fn do_receive_crowdloan_messages( + messages: Vec>, + ) -> Result<(), Error> { + todo!() + } +} + +// extrinsic code +impl Pallet { + pub fn do_unreserve_lease_deposit( + block: BlockNumberFor, + depositor: T::AccountId, + para_id: ParaId, + ) -> Result<(), Error> { + let balance = RcLeaseReserve::::take((block, &depositor, para_id)) + .ok_or(Error::::NoLeaseDeposit)?; + + let remaining = ::Currency::unreserve(&depositor, balance); + if remaining > 0 { + defensive!("Should be able to unreserve all"); + Self::deposit_event(Event::LeaseDepositUnreserveRemaining { + depositor, + remaining, + para_id, + }); + } + + Ok(()) + } + + pub fn do_withdraw_crowdloan_contribution( + block: BlockNumberFor, + depositor: T::AccountId, + para_id: ParaId, + ) -> Result<(), Error> { + // TODO remember to reactivate balance + let (pot, contribution) = RcCrowdloanContribution::::take((block, &depositor, para_id)) + .ok_or(Error::::NoCrowdloanContribution)?; + + // Maybe this is the first one to withdraw and we need to unreserve it from the pot + match Self::do_unreserve_lease_deposit(block, pot.clone(), para_id) { + Ok(()) => (), + Err(Error::::NoLeaseDeposit) => (), // fine + Err(e) => return Err(e), + } + + // Ideally this does not fail. But if it does, then we keep it for manual inspection. + let transferred = ::Currency::transfer( + &pot, + &depositor, + contribution, + Preservation::Preserve, + ) + .defensive() + .map_err(|_| Error::::FailedToWithdrawCrowdloanContribution)?; + defensive_assert!(transferred == contribution); + // Need to reactivate since we deactivated it here https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L793 + ::Currency::reactivate(transferred); + + Ok(()) + } +} diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index c56c513bfe..dc17102e2c 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -37,6 +37,7 @@ pub mod bounties; pub mod call; pub mod claims; pub mod conviction_voting; +pub mod crowdloan; pub mod indices; pub mod multisig; pub mod preimage; @@ -49,12 +50,14 @@ 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::{ pallet_prelude::*, storage::{transactional::with_transaction_opaque_err, TransactionOutcome}, traits::{ - fungible::{InspectFreeze, Mutate, MutateFreeze, MutateHold}, + fungible::{InspectFreeze, Mutate, MutateFreeze, MutateHold, Unbalanced}, Defensive, DefensiveTruncateFrom, LockableCurrency, OriginTrait, QueryPreimage, ReservableCurrency, StorePreimage, WithdrawReasons as LockWithdrawReasons, }, @@ -65,6 +68,7 @@ use pallet_rc_migrator::{ accounts::Account as RcAccount, claims::RcClaimsMessageOf, conviction_voting::RcConvictionVotingMessageOf, + crowdloan::RcCrowdloanMessageOf, indices::RcIndicesIndexOf, multisig::*, preimage::*, @@ -106,6 +110,8 @@ pub enum PalletEventName { Bounties, } +pub type BalanceOf = ::Balance; + #[frame_support::pallet(dev_mode)] pub mod pallet { use super::*; @@ -139,6 +145,7 @@ pub mod pallet { + MutateHold + InspectFreeze + MutateFreeze + + Unbalanced + ReservableCurrency + LockableCurrency; /// XCM check account. @@ -182,6 +189,56 @@ pub mod pallet { pub type RcAccounts = StorageMap<_, Twox64Concat, T::AccountId, RcAccountFor, OptionQuery>; + /// Amount of balance that was reserved for winning a lease auction. + /// + /// `unreserve_lease_deposit` can be permissionlessly called once the block number passed to + /// unreserve the deposit. It is implicitly called by `withdraw_crowdloan_contribution`. + /// + /// The account here can either be a crowdloan account or a solo bidder. If it is a crowdloan + /// account, then the summed up contributions for it in the contributions map will equate the + /// reserved balance here. + /// + /// The keys are as follows: + /// - Block number after which the deposit can be unreserved. + /// - The account that will have the balance unreserved. + /// - The para_id of the lease slot. + /// - The balance to be unreserved. + #[pallet::storage] + pub type RcLeaseReserve = StorageNMap< + _, + ( + NMapKey>, + NMapKey, + NMapKey, + ), + BalanceOf, + OptionQuery, + >; + + /// Amount of balance that a contributor made towards a crowdloan. + /// + /// `withdraw_crowdloan_contribution` can be permissionlessly called once the block number + /// passed to unlock the balance for a specific account. + /// + /// The keys are as follows: + /// - Block number after which the balance can be unlocked. + /// - The account that made the contribution. + /// - The para_id of the crowdloan. + /// + /// The value is (fund_pot, balance). The contribution pot is the second key in the + /// `RcCrowdloanContribution` storage. + #[pallet::storage] + pub type RcCrowdloanContribution = StorageNMap< + _, + ( + NMapKey>, + NMapKey, + NMapKey, + ), + (T::AccountId, BalanceOf), + OptionQuery, + >; + #[pallet::error] pub enum Error { /// The error that should to be replaced by something meaningful. @@ -202,6 +259,12 @@ pub mod pallet { /// Failed to integrate a vesting schedule. FailedToIntegrateVestingSchedule, Unreachable, + /// Either no lease deposit or already unreserved. + NoLeaseDeposit, + /// Either no crowdloan contribution or already withdrawn. + NoCrowdloanContribution, + /// Failed to withdraw crowdloan contribution. + FailedToWithdrawCrowdloanContribution, } #[pallet::event] @@ -210,6 +273,13 @@ pub mod pallet { /// The event that should to be replaced by something meaningful. TODO, + /// Some amount could not be unreserved and possibly needs manual cleanup. + LeaseDepositUnreserveRemaining { + depositor: T::AccountId, + para_id: ParaId, + remaining: BalanceOf, + }, + /// We received a batch of accounts that we are going to integrate. AccountBatchReceived { /// How many accounts are in the batch. @@ -602,6 +672,42 @@ pub mod pallet { Self::do_receive_asset_rates(rates).map_err(Into::into) } + + #[pallet::call_index(19)] + pub fn unreserve_lease_deposit( + origin: OriginFor, + block: BlockNumberFor, + depositor: Option, + para_id: ParaId, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let depositor = depositor.unwrap_or(sender); + + Self::do_unreserve_lease_deposit(block, depositor, para_id).map_err(Into::into) + } + + #[pallet::call_index(20)] + pub fn withdraw_crowdloan_contribution( + origin: OriginFor, + block: BlockNumberFor, + depositor: Option, + para_id: ParaId, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let depositor = depositor.unwrap_or(sender); + + Self::do_withdraw_crowdloan_contribution(block, depositor, para_id).map_err(Into::into) + } + + #[pallet::call_index(21)] + pub fn receive_crowdloan_messages( + origin: OriginFor, + messages: Vec>, + ) -> DispatchResult { + ensure_root(origin)?; + + Self::do_receive_crowdloan_messages(messages).map_err(Into::into) + } } #[pallet::hooks] diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index 5c5dbf20f4..e41be256b4 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -1,29 +1,155 @@ -// notes: -// - deposit from the funds map need to be unreserved to the parachain manager https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L416-L417 -// - when refunding crowdloan contributions, we need to re-activate the issuance https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L792-L793 -// - burn remaining funds in the crowdloan account https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L573-L574 - -/* - -Crowdloan 2008 - -{ - depositor: 12xWvNmBVt5541brRVANFaFejMrttu8tnBso3NgzsSuZnY7f - verifier: null - deposit: 5,000,000,000,000 - raised: 2,220,000,000,000 - end: 21,413,000 - cap: 500,000,000,000,000 - lastContribution: { - Ending: 21,055,050 - } - firstPeriod: 17 - lastPeriod: 24 - fundIndex: 91 +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO FAIL-CI: Insecure unless your chain includes `PrevalidateAttests` as a +// `TransactionExtension`. + +use crate::{types::AccountIdOf, *}; + +pub struct CrowdloanMigrator { + _marker: sp_std::marker::PhantomData, } -https://polkadot.subscan.io/crowdloan/2008-44 +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, Clone, PartialEq, Eq)] +pub enum RcCrowdloanMessage { + LeaseReserve { + block: BlockNumber, + account: AccountId, + para_id: ParaId, + amount: Balance, + }, + CrowdloanContribution { + block: BlockNumber, + contributor: AccountId, + para_id: ParaId, + amount: Balance, + crowdloan_account: AccountId, + }, +} + +pub type RcCrowdloanMessageOf = + RcCrowdloanMessage, AccountIdOf, crate::BalanceOf>; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, Clone, PartialEq, Eq)] +pub enum CrowdloanStage { + LeaseReserve { last_key: Option }, + CrowdloanContribution { last_key: Option }, + Finished, +} + +impl PalletMigration for CrowdloanMigrator +where crate::BalanceOf: From<<::Currency as frame_support::traits::Currency>::Balance> { + type Key = CrowdloanStage; + type Error = Error; + + fn migrate_many( + current_key: Option, + weight_counter: &mut WeightMeter, + ) -> Result, Self::Error> { + let mut inner_key = current_key.unwrap_or(CrowdloanStage::LeaseReserve { last_key: None }); + let mut messages = Vec::new(); + + loop { + if weight_counter + .try_consume(::DbWeight::get().reads_writes(1, 1)) + .is_err() + { + if messages.is_empty() { + return Err(Error::OutOfWeight); + } else { + break; + } + } + + if messages.len() > 10_000 { + log::warn!("Weight allowed very big batch, stopping"); + break; + } + + inner_key = match inner_key { + CrowdloanStage::LeaseReserve { last_key } => { + let mut iter = match last_key.clone() { + Some(last_key) => pallet_slots::Leases::::iter_from_key(last_key), + None => pallet_slots::Leases::::iter(), + }; + + match iter.next() { + Some((para_id, leases)) => { + pallet_slots::Leases::::remove(¶_id); -Ended + 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); + continue; + }; -*/ + let Some((lease_acc, _)) = last_lease else { + defensive!("Last lease cannot be None"); + 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(); + + if amount == 0 { + // fucking stupid ParaId type + defensive_assert!(para_id < ParaId::from(2000), "Must be system chain"); + continue; + } + + let unlock_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 }); + CrowdloanStage::LeaseReserve { last_key: Some(para_id) } + }, + None => CrowdloanStage::CrowdloanContribution { last_key: None }, + } + }, + CrowdloanStage::CrowdloanContribution { last_key } => { + todo!() + }, + CrowdloanStage::Finished => { + break; + }, + } + } + + if !messages.is_empty() { + Pallet::::send_chunked_xcm(messages, |messages| { + types::AhMigratorCall::::ReceiveCrowdloanMessages { messages } + })?; + } + + if inner_key == CrowdloanStage::Finished { + Ok(None) + } else { + Ok(Some(inner_key)) + } + } +} + +/// Calculate the lease ending block from the number of remaining leases (including the current). +fn num_leases_to_ending_block(num_leases: u32) -> BlockNumberFor { + let now = frame_system::Pallet::::block_number(); + let num_leases: BlockNumberFor = num_leases.into(); + let offset = ::LeaseOffset::get(); + let period = ::LeasePeriod::get(); + + let current_period = (now - offset) / period; + (current_period + num_leases) * period + offset +} diff --git a/pallets/rc-migrator/src/lib.rs b/pallets/rc-migrator/src/lib.rs index f3f414b3ef..0e49f0be89 100644 --- a/pallets/rc-migrator/src/lib.rs +++ b/pallets/rc-migrator/src/lib.rs @@ -33,6 +33,7 @@ pub mod accounts; pub mod claims; +pub mod crowdloan; pub mod indices; pub mod multisig; pub mod preimage; @@ -66,7 +67,9 @@ use indices::IndicesMigrator; use multisig::MultisigMigrator; use pallet_balances::AccountData; use polkadot_parachain_primitives::primitives::Id as ParaId; -use polkadot_runtime_common::{claims as pallet_claims, paras_registrar}; +use polkadot_runtime_common::{ + claims as pallet_claims, crowdloan as pallet_crowdloan, paras_registrar, slots as pallet_slots, +}; use preimage::{ PreimageChunkMigrator, PreimageLegacyRequestStatusMigrator, PreimageRequestStatusMigrator, }; @@ -121,6 +124,8 @@ pub enum PalletEventName { BagsList, } +pub type BalanceOf = ::Balance; + #[derive(Encode, Decode, Clone, Default, RuntimeDebug, TypeInfo, MaxEncodedLen, PartialEq, Eq)] pub enum MigrationStage { @@ -225,16 +230,25 @@ pub enum MigrationStage>, }, ConvictionVotingMigrationDone, + BountiesMigrationInit, BountiesMigrationOngoing { last_key: Option, }, BountiesMigrationDone, + AssetRateMigrationInit, AssetRateMigrationOngoing { last_key: Option, }, AssetRateMigrationDone, + + CrowdloanMigrationInit, + CrowdloanMigrationOngoing { + last_key: Option, + }, + CrowdloanMigrationDone, + MigrationDone, } @@ -265,6 +279,7 @@ impl Result { Ok(match s { "skip-accounts" => MigrationStage::AccountsMigrationDone, + "crowdloan" => MigrationStage::CrowdloanMigrationInit, "preimage" => MigrationStage::PreimageMigrationInit, "referenda" => MigrationStage::ReferendaMigrationInit, "multisig" => MigrationStage::MultisigMigrationInit, @@ -309,6 +324,8 @@ pub mod pallet { + pallet_bounties::Config + pallet_treasury::Config + pallet_asset_rate::Config + + pallet_slots::Config + + pallet_crowdloan::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -395,7 +412,8 @@ pub mod pallet { pub struct Pallet(_); #[pallet::hooks] - impl Hooks> for Pallet { + impl Hooks> for Pallet + where crate::BalanceOf: From<<::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(); @@ -991,9 +1009,40 @@ pub mod pallet { } }, MigrationStage::AssetRateMigrationDone => { - Self::transition(MigrationStage::MigrationDone); + Self::transition(MigrationStage::CrowdloanMigrationInit); }, + MigrationStage::CrowdloanMigrationInit => { + Self::transition(MigrationStage::CrowdloanMigrationOngoing { last_key: None }); + }, + MigrationStage::CrowdloanMigrationOngoing { last_key } => { + let res = with_transaction_opaque_err::, Error, _>(|| { + match crowdloan::CrowdloanMigrator::::migrate_many( + last_key, + &mut weight_counter, + ) { + Ok(last_key) => TransactionOutcome::Commit(Ok(last_key)), + Err(e) => TransactionOutcome::Rollback(Err(e)), + } + }) + .expect("Always returning Ok; qed"); + match res { + Ok(None) => { + Self::transition(MigrationStage::CrowdloanMigrationDone); + }, + Ok(Some(last_key)) => { + Self::transition(MigrationStage::CrowdloanMigrationOngoing { + last_key: Some(last_key), + }); + }, + e => { + defensive!("Error while migrating crowdloan: {:?}", e); + }, + } + }, + MigrationStage::CrowdloanMigrationDone => { + Self::transition(MigrationStage::MigrationDone); + }, MigrationStage::MigrationDone => (), }; diff --git a/pallets/rc-migrator/src/types.rs b/pallets/rc-migrator/src/types.rs index 98bb8e2e99..7e4947f95f 100644 --- a/pallets/rc-migrator/src/types.rs +++ b/pallets/rc-migrator/src/types.rs @@ -76,6 +76,8 @@ pub enum AhMigratorCall { ReceiveBountiesMessages { messages: Vec> }, #[codec(index = 18)] ReceiveAssetRates { asset_rates: Vec<(::AssetKind, FixedU128)> }, + #[codec(index = 21)] + ReceiveCrowdloanMessages { messages: Vec> }, } /// Copy of `ParaInfo` type from `paras_registrar` pallet. From 31ca5cdf98c798c9974af11b931e87f063e23804 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 12 Feb 2025 23:17:37 +0100 Subject: [PATCH 04/22] fmt Signed-off-by: Oliver Tale-Yazdi --- pallets/rc-migrator/src/crowdloan.rs | 10 +++++----- pallets/rc-migrator/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index e41be256b4..994ce3e3c9 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -55,7 +55,7 @@ impl PalletMigration for CrowdloanMigrator where crate::BalanceOf: From<<::Currency as frame_support::traits::Currency>::Balance> { type Key = CrowdloanStage; type Error = Error; - + fn migrate_many( current_key: Option, weight_counter: &mut WeightMeter, @@ -74,19 +74,19 @@ where crate::BalanceOf: From<<:: break; } } - + if messages.len() > 10_000 { log::warn!("Weight allowed very big batch, stopping"); break; } - + inner_key = match inner_key { CrowdloanStage::LeaseReserve { last_key } => { let mut iter = match last_key.clone() { Some(last_key) => pallet_slots::Leases::::iter_from_key(last_key), None => pallet_slots::Leases::::iter(), }; - + match iter.next() { Some((para_id, leases)) => { pallet_slots::Leases::::remove(¶_id); @@ -114,7 +114,7 @@ where crate::BalanceOf: From<<:: let unlock_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 }); + messages.push(RcCrowdloanMessage::LeaseReserve { block: unlock_block, account: lease_acc.clone(), para_id, amount }); CrowdloanStage::LeaseReserve { last_key: Some(para_id) } }, None => CrowdloanStage::CrowdloanContribution { last_key: None }, diff --git a/pallets/rc-migrator/src/lib.rs b/pallets/rc-migrator/src/lib.rs index 0e49f0be89..6b1dc4d2d3 100644 --- a/pallets/rc-migrator/src/lib.rs +++ b/pallets/rc-migrator/src/lib.rs @@ -412,7 +412,7 @@ pub mod pallet { pub struct Pallet(_); #[pallet::hooks] - impl Hooks> for Pallet + impl Hooks> for Pallet where crate::BalanceOf: From<<::Currency as frame_support::traits::Currency>::Balance>{ fn on_initialize(_: BlockNumberFor) -> Weight { let mut weight_counter = WeightMeter::with_limit(T::MaxRcWeight::get()); From 4384e584945a5ea7d7be10161b9c2c3af3909de7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 13 Feb 2025 20:09:18 +0100 Subject: [PATCH 05/22] shit works now Signed-off-by: Oliver Tale-Yazdi --- integration-tests/ahm/src/tests.rs | 3 + pallets/ah-migrator/src/crowdloan.rs | 79 ++++++++++++++++++++- pallets/ah-migrator/src/lib.rs | 22 +++++- pallets/rc-migrator/src/crowdloan.rs | 102 +++++++++++++++++++++++---- pallets/rc-migrator/src/lib.rs | 7 +- 5 files changed, 195 insertions(+), 18 deletions(-) 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(); From bbab0957d5907886f752ce071c8f18206f0cd837 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 13 Feb 2025 21:08:14 +0100 Subject: [PATCH 06/22] cleanup Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 2 - Cargo.toml | 3 +- integration-tests/ahm/Cargo.toml | 2 - integration-tests/ahm/src/tests.rs | 267 +-------------------------- pallets/ah-migrator/src/crowdloan.rs | 2 +- pallets/rc-migrator/src/crowdloan.rs | 43 +++-- 6 files changed, 36 insertions(+), 283 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 543a1c36ba..ae9799ddf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10073,13 +10073,11 @@ name = "polkadot-integration-tests-ahm" version = "1.0.0" dependencies = [ "asset-hub-polkadot-runtime", - "chrono", "cumulus-primitives-core", "emulated-integration-tests-common", "frame-remote-externalities", "frame-support", "frame-system", - "hex", "log", "pallet-ah-migrator", "pallet-balances", diff --git a/Cargo.toml b/Cargo.toml index 2f0ad52e96..764768758c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ pallet-rc-migrator = { path = "pallets/rc-migrator", default-features = false } pallet-ah-migrator = { path = "pallets/ah-migrator", default-features = false } assert_matches = { version = "1.5.0" } approx = { version = "0.5.1" } -chrono = { version = "0.4.39", default-features = false } asset-hub-kusama-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-kusama" } asset-hub-kusama-runtime = { path = "system-parachains/asset-hubs/asset-hub-kusama" } asset-hub-polkadot-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-polkadot" } @@ -321,7 +320,7 @@ members = [ panic = "unwind" opt-level = 3 # AHM: FAIL-CI -debug-assertions = false +debug-assertions = true overflow-checks = true [profile.production] diff --git a/integration-tests/ahm/Cargo.toml b/integration-tests/ahm/Cargo.toml index 4df7775ede..daf1e678ab 100644 --- a/integration-tests/ahm/Cargo.toml +++ b/integration-tests/ahm/Cargo.toml @@ -14,12 +14,10 @@ authority-discovery-primitives = { workspace = true, default-features = true } babe-primitives = { workspace = true, default-features = true } beefy-primitives = { workspace = true, default-features = true } cumulus-primitives-core = { workspace = true, default-features = true } -chrono = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } frame-support = { workspace = true, default-features = true } frame-system = { workspace = true, default-features = true } grandpa = { workspace = true } -hex = { workspace = true, default-features = true } log = { workspace = true, default-features = true } pallet-rc-migrator = { workspace = true, default-features = true } pallet-ah-migrator = { workspace = true, default-features = true } diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index 7689d763bb..fcd669c6dd 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -32,26 +32,16 @@ //! ``` use asset_hub_polkadot_runtime::Runtime as AssetHub; -use codec::Encode; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::traits::*; -use frame_system::pallet_prelude::BlockNumberFor; use pallet_rc_migrator::{types::PalletMigrationChecks, MigrationStage, RcMigrationStage}; -use parachains_common::impls::BalanceOf; -use polkadot_runtime::{Block as PolkadotBlock, Runtime as Polkadot}; -use polkadot_runtime_common::{ - crowdloan as pallet_crowdloan, paras_registrar, paras_registrar as pallet_registrar, - slots as pallet_slots, -}; -use std::{collections::BTreeMap, io::Write, str::FromStr}; +use polkadot_runtime::Runtime as Polkadot; +use std::str::FromStr; 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); @@ -122,256 +112,3 @@ async fn account_migration_works() { // some overweight ones. }); } - -use sp_runtime::{traits::Dispatchable, AccountId32}; - -/// Check that our function to calculate the unlock time of a crowdloan contribution is correct. -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn crowdloan_unlock_times_are_correct_works() { - std::env::set_var("SNAP_RC", "/Users/vados/Documents/work/runtimes/polkadot.snap"); - std::env::set_var("START_STAGE", "preimage"); - - let mut rc = remote_ext_test_setup::("SNAP_RC").await.unwrap(); - - rc.execute_with(|| { - let now = frame_system::Pallet::::block_number(); - let mut para_ids = pallet_crowdloan::Funds::::iter_keys().collect::>(); - para_ids.sort(); - - for para_id in para_ids.clone() { - let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); - assert!(fund.end < now); - - let id: u32 = para_id.into(); - let fund_id = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); - - let acc = frame_system::Account::::get(&fund_id); - let total = acc.data.free + acc.data.reserved; - - // All crowdloans without reserved amount can be dissolved. We will do this as part of - // the migration - if acc.data.reserved == 0 { - println!( - "$ Para: {}, Fund index: {}, id: {} Can be dissolved", - para_id, fund.fund_index, fund_id - ); - - assert!(fund.raised == 0); - assert!(fund.deposit != 0, "we must have a deposit"); // TODO refund - ensure_can_dissolve(para_id); - - continue; - } - - println!( - " Para: {}, Fund index: {}, id: {}, with {} total", - para_id, - fund.fund_index, - fund_id, - total / 10_000_000_000 - ); - } - - println!("#### Done ####"); - let mut refunds: String = "para_id,fund_id,account,amount,refund_date\n".into(); - - /*for para_id in para_ids { - let Some(fund) = pallet_crowdloan::Funds::::get(para_id) else { - continue; - }; - - let id: u32 = para_id.into(); - let fund_id = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); - - let acc = frame_system::Account::::get(&fund_id); - let total = acc.data.free + acc.data.reserved;*/ - let refund_time = calculate_refund_time(para_ids); - - /*let mut contrib_iter = pallet_crowdloan::Pallet::::contribution_iterator(fund.fund_index); - let mut total_contrib = 0; - let mut num_contribs = 0; - - while let Some((contributor, (contrib, _memo))) = contrib_iter.next() { - total_contrib += contrib; - num_contribs += 1; - let amount = format!("{}", contrib / 10_000_000_000).replace(",", "_"); - refunds.push_str(&format!("{},{},{},{},{}\n", para_id, fund.fund_index, contributor, amount, refund_time)); - } - assert_eq!(total_contrib, fund.raised); - - println!(" Para: {}, Fund index: {}, id: {}, with {} total, {} contribs", para_id, fund.fund_index, fund_id, total / 10_000_000_000, num_contribs); - if acc.data.free + acc.data.reserved > fund.raised { - println!("! Over funded by {}", (acc.data.free + acc.data.reserved - fund.raised) / 10_000_000_000); - } - }*/ - - // write to file - //let mut file = - // std::fs::File::create("/Users/vados/Documents/work/runtimes/refunds.csv").unwrap(); - // file.write_all(refunds.as_bytes()).unwrap(); - }); -} - -/// Calculate when a crowdloan will be able to dissolve. -fn calculate_refund_time(mut para_ids: Vec) -> BTreeMap> { - let mut cutoff = 10_000_000; // some high number to make the test timeout if there is an error - let mut original_reserved: BTreeMap> = BTreeMap::new(); - let orig_len = para_ids.len(); - let mut refund_times: BTreeMap> = BTreeMap::new(); - - frame_support::hypothetically!({ - while !para_ids.is_empty() && cutoff > 0 { - let now = frame_system::Pallet::::block_number(); - - para_ids = para_ids - .into_iter() - .filter(|para_id| { - let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); - let slots = pallet_slots::Leases::::get(para_id); - // Lease pot account can either be a crowdloan account or a solo bidder - log::info!("[{now}] Para {} has {} slots", para_id, slots.len()); - let Some(last_lease) = slots.last().cloned() else { - // TODO check this - // TODO additional check with crowdloan account has no reserve - log::info!( - "[{now}] Para {} has no slots and already had its funds unreserved", - para_id - ); - // TODO https://polkadot.subsquare.io/referenda/524 - if *para_id == ParaId::from(3356) { - return false; - } - refund_times.insert(*para_id, now); - // TODO some additional checks i forgot about - // Account must have at least `rased` in free funds - let pot = - pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); - let pot_acc = frame_system::Account::::get(&pot); - if pot_acc.data.free < fund.raised { - panic!( - "Para {} has {} raised but only {} free", - para_id, fund.raised, pot_acc.data.free - ); - } - return false; - }; - - let Some((lease_pot_account, deposit_amount)) = last_lease else { - frame_support::defensive!("Last lease should never be None"); - return false; - }; - - let reserved = - pallet_balances::Pallet::::reserved_balance(&lease_pot_account); - let original_res = - original_reserved.entry(lease_pot_account).or_insert(reserved); - if reserved < *original_res { - log::info!( - "[{}] Lease funds of para {} can be withdrawn, reserved: {} -> {}", - now, - para_id, - *original_res, - reserved - ); - assert_eq(*original_res - reserved, deposit_amount); - assert_eq(fund.raised, 0); - // TODO additional checks if there is a crowdloan, then it should be zero - return false; - } - - true - }) - .collect(); - - // Go to the start of the next Lease period - let offset = ::LeaseOffset::get(); - let period = ::LeasePeriod::get(); - let next_period_start = ((now - offset) / period) * period + period + offset; - - run_to_block(next_period_start); - cutoff -= 1; - } - - if !para_ids.is_empty() { - panic!("Some crowdloans could not be dissolved: {:?}", para_ids); - } - }); - // TODO -1 for the Bifrost lease swap 3356 mentioned above - assert_eq!(orig_len - 1, refund_times.len()); - refund_times -} - -fn block_number_to_date(block_number: u32) -> String { - let block_now = frame_system::Pallet::::block_number(); - let unix_now = pallet_timestamp::Now::::get(); - let date = unix_now as i128 + (block_number as i128 - block_now as i128) as i128 * 6_000; - chrono::NaiveDateTime::from_timestamp_millis(date as i64).unwrap().to_string() -} - -fn ensure_can_refund(para_id: ParaId, at: u32) { - frame_support::hypothetically!({ - let alice = AccountId32::new([0u8; 32]); - pallet_balances::Pallet::::make_free_balance_be(&alice, 100_000_000_000_000_000); - run_to_block(at); - - let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); - let origin = polkadot_runtime::RuntimeOrigin::signed(alice); - let call: polkadot_runtime::RuntimeCall = - pallet_crowdloan::Call::::refund { index: para_id }.into(); - call.dispatch_bypass_filter(origin).unwrap(); // Why bypass? - }); -} - -fn run_to_block(at: u32) { - //let mut bn = frame_system::Pallet::::block_number(); - - frame_system::Pallet::::set_block_number(at); - //polkadot_runtime::AllPalletsWithSystem::on_initialize(bn); - as frame_support::traits::OnInitialize< - BlockNumberFor, - >>::on_initialize(at); - as frame_support::traits::OnInitialize< - BlockNumberFor, - >>::on_initialize(at); - as frame_support::traits::OnInitialize< - BlockNumberFor, - >>::on_initialize(at); - as frame_support::traits::OnFinalize< - BlockNumberFor, - >>::on_finalize(at); -} - -fn ensure_can_dissolve(para_id: ParaId) { - frame_support::hypothetically!({ - let fund = pallet_crowdloan::Funds::::get(para_id).unwrap(); - let sender = fund.depositor; - println!("fund index: {}, sender: {}, deposit: {}", fund.fund_index, sender, fund.deposit); - let data_before = frame_system::Account::::get(&sender).data; - let origin = polkadot_runtime::RuntimeOrigin::signed(sender.clone()); - - let call: polkadot_runtime::RuntimeCall = - pallet_crowdloan::Call::::dissolve { index: para_id }.into(); - call.dispatch(origin).unwrap(); - let data_after = frame_system::Account::::get(&sender).data; - - if data_after.reserved >= data_before.reserved || data_after.free <= data_before.free { - println!("! Could not unreserve"); - } - }); -} - -// The block after which a crowdloan contribution will be able to redeem their contribution. -/*fn crowdloan_unlock_block(para_id: ParaId) -> u64 { - let lease_period = T::LeasePeriod::get(); - let fund_index = T::FundIndex::get(); - let fund_period = fund_index / lease_period; - lease_period * fund_period + fund_index -} -*/ - -/// Assert that also works without debug_assert -fn assert_eq(a: R, b: R) { - if a != b { - panic!("{a:?} != {b:?}"); - } -} diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index 4d7c6a963e..5531f4ead5 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -88,7 +88,7 @@ impl Pallet { 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(( + defensive_assert!(!RcCrowdloanReserve::::contains_key(( unreserve_block, &depositor, para_id diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index a40c62a31d..ee9c8b7d99 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -55,6 +55,7 @@ pub type RcCrowdloanMessageOf = #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, Clone, PartialEq, Eq)] pub enum CrowdloanStage { + Setup, LeaseReserve { last_key: Option }, CrowdloanContribution { last_key: Option }, CrowdloanDeposit, @@ -75,7 +76,7 @@ impl PalletMigration for CrowdloanMigrator current_key: Option, weight_counter: &mut WeightMeter, ) -> Result, Self::Error> { - let mut inner_key = current_key.unwrap_or(CrowdloanStage::LeaseReserve { last_key: None }); + let mut inner_key = current_key.unwrap_or(CrowdloanStage::Setup); let mut messages = Vec::new(); loop { @@ -83,13 +84,11 @@ impl PalletMigration for CrowdloanMigrator .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 { @@ -98,6 +97,24 @@ impl PalletMigration for CrowdloanMigrator } inner_key = match inner_key { + CrowdloanStage::Setup => { + inner_key = CrowdloanStage::LeaseReserve { last_key: None }; + + // Only thing to do here is to re-the bifrost crowdloan: https://polkadot.subsquare.io/referenda/524 + let leases = pallet_slots::Leases::::take(ParaId::from(2030)); + if leases.is_empty() { + defensive!("Bifrost fund maybe already ended, remove this"); + continue; + } + + // It would be better if we can re-map all contributions to the new para id, but + // that requires to iterate them all so we go the other way around; change the + // new leases to the old Bifrost Crowdloan. + pallet_slots::Leases::::insert(ParaId::from(3356), leases); + log::info!(target: LOG_TARGET, "Migrated Bifrost Leases from crowdloan 2030 to 3356"); + + inner_key + }, CrowdloanStage::LeaseReserve { last_key } => { let mut iter = match last_key.clone() { Some(last_key) => pallet_slots::Leases::::iter_from_key(last_key), @@ -130,7 +147,7 @@ impl PalletMigration for CrowdloanMigrator 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: {:?}, unreserve_block: {:?}", ¶_id, &lease_acc, &amount, &unreserve_block); + log::debug!(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) } }, @@ -162,13 +179,15 @@ impl PalletMigration for CrowdloanMigrator let leases = pallet_slots::Leases::::get(para_id); if leases.is_empty() { - defensive!("Leases should not be empty if there is a fund"); + defensive_assert!(fund.raised == 0u32.into(), "Fund should be empty"); + inner_key = CrowdloanStage::CrowdloanContribution { last_key: Some(para_id) }; + continue; } 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); + log::debug!(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 }); @@ -182,17 +201,19 @@ impl PalletMigration for CrowdloanMigrator CrowdloanStage::CrowdloanDeposit => { match pallet_crowdloan::Funds::::iter().next() { Some((para_id, fund)) => { + inner_key = CrowdloanStage::CrowdloanDeposit; 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"); + defensive_assert!(fund.raised == 0u32.into(), "Fund should be empty"); + continue; } 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); + log::debug!(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 + inner_key }, None => CrowdloanStage::Finished, } From ae2f278f353c208e56ec3d4300d96edf7e8a9d24 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 13 Feb 2025 21:31:43 +0100 Subject: [PATCH 07/22] cleanup Signed-off-by: Oliver Tale-Yazdi --- pallets/ah-migrator/src/crowdloan.rs | 62 +++++++++++++++++----------- pallets/ah-migrator/src/lib.rs | 42 +++++++++++-------- pallets/rc-migrator/src/crowdloan.rs | 33 +++++++-------- pallets/rc-migrator/src/types.rs | 2 +- 4 files changed, 77 insertions(+), 62 deletions(-) diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index 5531f4ead5..d913cf2c09 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -1,19 +1,17 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . use crate::*; use frame_support::traits::tokens::Preservation; @@ -110,16 +108,12 @@ impl Pallet { para_id: ParaId, ) -> Result<(), Error> { let balance = RcLeaseReserve::::take((block, &depositor, para_id)) - .ok_or(Error::::NoLeaseDeposit)?; + .ok_or(Error::::NoLeaseReserve)?; let remaining = ::Currency::unreserve(&depositor, balance); if remaining > 0 { defensive!("Should be able to unreserve all"); - Self::deposit_event(Event::LeaseDepositUnreserveRemaining { - depositor, - remaining, - para_id, - }); + Self::deposit_event(Event::LeaseUnreserveRemaining { depositor, remaining, para_id }); } Ok(()) @@ -130,14 +124,13 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { - // TODO remember to reactivate balance let (pot, contribution) = RcCrowdloanContribution::::take((block, &depositor, para_id)) .ok_or(Error::::NoCrowdloanContribution)?; // Maybe this is the first one to withdraw and we need to unreserve it from the pot match Self::do_unreserve_lease_deposit(block, pot.clone(), para_id) { Ok(()) => (), - Err(Error::::NoLeaseDeposit) => (), // fine + Err(Error::::NoLeaseReserve) => (), // fine Err(e) => return Err(e), } @@ -156,4 +149,25 @@ impl Pallet { Ok(()) } + + pub fn do_unreserve_crowdloan_reserve( + block: BlockNumberFor, + depositor: T::AccountId, + para_id: ParaId, + ) -> Result<(), Error> { + let amount = RcCrowdloanReserve::::take((block, &depositor, para_id)) + .ok_or(Error::::NoCrowdloanReserve)?; + + let remaining = ::Currency::unreserve(&depositor, amount); + if remaining > 0 { + defensive!("Should be able to unreserve all"); + Self::deposit_event(Event::CrowdloanUnreserveRemaining { + depositor, + remaining, + para_id, + }); + } + + Ok(()) + } } diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index 0fbda4bd4d..74bf1cdc4f 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -280,9 +280,10 @@ pub mod pallet { FailedToIntegrateVestingSchedule, Unreachable, /// Either no lease deposit or already unreserved. - NoLeaseDeposit, + NoLeaseReserve, /// Either no crowdloan contribution or already withdrawn. NoCrowdloanContribution, + NoCrowdloanReserve, /// Failed to withdraw crowdloan contribution. FailedToWithdrawCrowdloanContribution, } @@ -293,8 +294,15 @@ pub mod pallet { /// The event that should to be replaced by something meaningful. TODO, - /// Some amount could not be unreserved and possibly needs manual cleanup. - LeaseDepositUnreserveRemaining { + /// Some lease reserve could not be unreserved and needs manual cleanup. + LeaseUnreserveRemaining { + depositor: T::AccountId, + para_id: ParaId, + remaining: BalanceOf, + }, + + /// Some amount for a crowdloan reserve could not be unreserved and needs manual cleanup. + CrowdloanUnreserveRemaining { depositor: T::AccountId, para_id: ParaId, remaining: BalanceOf, @@ -441,21 +449,6 @@ pub mod pallet { /// How many scheduler messages failed to integrate. count_bad: u32, }, - /// Should not happen. Manual intervention by the Fellowship required. - /// - /// Can happen when existing AH and incoming RC vesting schedules have more combined - /// entries than allowed. This triggers the merging logic which has henceforth failed - /// with the given inner pallet-vesting error. - FailedToMergeVestingSchedules { - /// The account that failed to merge the schedules. - who: AccountId32, - /// The first schedule index that failed to merge. - schedule1: u32, - /// The second schedule index that failed to merge. - schedule2: u32, - /// The index of the pallet-vesting error that occurred. - pallet_vesting_error_index: Option, - }, ConvictionVotingMessagesReceived { /// How many conviction voting messages are in the batch. count: u32, @@ -720,6 +713,19 @@ pub mod pallet { } #[pallet::call_index(21)] + pub fn unreserve_crowdloan_reserve( + origin: OriginFor, + block: BlockNumberFor, + depositor: Option, + para_id: ParaId, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let depositor = depositor.unwrap_or(sender); + + Self::do_unreserve_crowdloan_reserve(block, depositor, para_id).map_err(Into::into) + } + + #[pallet::call_index(22)] pub fn receive_crowdloan_messages( origin: OriginFor, messages: Vec>, diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index ee9c8b7d99..315a7d9aa8 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -1,22 +1,17 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// TODO FAIL-CI: Insecure unless your chain includes `PrevalidateAttests` as a -// `TransactionExtension`. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . use crate::{types::AccountIdOf, *}; diff --git a/pallets/rc-migrator/src/types.rs b/pallets/rc-migrator/src/types.rs index 7e4947f95f..09084fce8b 100644 --- a/pallets/rc-migrator/src/types.rs +++ b/pallets/rc-migrator/src/types.rs @@ -76,7 +76,7 @@ pub enum AhMigratorCall { ReceiveBountiesMessages { messages: Vec> }, #[codec(index = 18)] ReceiveAssetRates { asset_rates: Vec<(::AssetKind, FixedU128)> }, - #[codec(index = 21)] + #[codec(index = 22)] ReceiveCrowdloanMessages { messages: Vec> }, } From 88e39dc5e99597cfabac88f926b5683830417e56 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 13 Feb 2025 23:42:24 +0100 Subject: [PATCH 08/22] docs Signed-off-by: Oliver Tale-Yazdi --- pallets/ah-migrator/src/crowdloan.rs | 5 +- pallets/ah-migrator/src/lib.rs | 23 ++++++++ pallets/rc-migrator/src/crowdloan.rs | 78 +++++++++++++++++++--------- 3 files changed, 78 insertions(+), 28 deletions(-) diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index d913cf2c09..40720a7694 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -78,14 +78,13 @@ impl Pallet { (crowdloan_account, amount), ); }, - RcCrowdloanMessage::CrowdloanDeposit { + RcCrowdloanMessage::CrowdloanReserve { 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); + log::info!(target: LOG_TARGET, "Integrating crowdloan reserve for para_id: {:?}, amount: {:?}, depositor: {:?}", ¶_id, &amount, &depositor); defensive_assert!(!RcCrowdloanReserve::::contains_key(( unreserve_block, &depositor, diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index 74bf1cdc4f..ee5c906db9 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -686,6 +686,14 @@ pub mod pallet { Self::do_receive_asset_rates(rates).map_err(Into::into) } + /// Unreserve the deposit that was taken for creating a crowdloan. + /// + /// This can be called by any signed origin. It unreserves the lease deposit on the account + /// that won the lease auction. It can be unreserved once all leases expired. Note that it + /// will be called automatically from `withdraw_crowdloan_contribution` for the matching + /// crowdloan account. + /// + /// Solo bidder accounts that won lease auctions can use this to unreserve their amount. #[pallet::call_index(19)] pub fn unreserve_lease_deposit( origin: OriginFor, @@ -699,6 +707,13 @@ pub mod pallet { Self::do_unreserve_lease_deposit(block, depositor, para_id).map_err(Into::into) } + /// Withdraw the contribution of a finished crowdloan. + /// + /// A crowdloan contribution can be withdrawn if either: + /// - The crowdloan failed to in an auction and timed out + /// - Won an auction and all leases expired + /// + /// Can be called by any signed origin. #[pallet::call_index(20)] pub fn withdraw_crowdloan_contribution( origin: OriginFor, @@ -712,6 +727,14 @@ pub mod pallet { Self::do_withdraw_crowdloan_contribution(block, depositor, para_id).map_err(Into::into) } + /// Unreserve the deposit that was taken for creating a crowdloan. + /// + /// This can be called once: + /// - The crowdloan failed to win an auction and timed out + /// - Won an auction and all leases expired + /// + /// Can be called by any signed origin. The condition that all contributions are withdrawn + /// is not checked anymore since all withdraw and unreserve functions are permissionless. #[pallet::call_index(21)] pub fn unreserve_crowdloan_reserve( origin: OriginFor, diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index 315a7d9aa8..f55e1b5ed0 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -21,26 +21,55 @@ pub struct CrowdloanMigrator { #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, Clone, PartialEq, Eq)] pub enum RcCrowdloanMessage { + /// Reserve for some slot leases. LeaseReserve { /// The block number at which this deposit can be unreserved. unreserve_block: BlockNumber, + /// Account that has `amount` reserved. account: AccountId, + /// Parachain ID that this crowdloan belongs to. + /// + /// Note that Bifrost ID 3356 is now 2030. para_id: ParaId, + /// Amount that was reserved for the lease. + /// + /// This is not necessarily the same as the full crowdloan contribution amount, since there + /// can be contributions after the lease candle auction ended. But it is the same for solo + /// bidders. The amount that was contributed after the cutoff will be held as *free* by the + /// crowdloan pot account. amount: Balance, }, + /// Contribute to a crowdloan. CrowdloanContribution { /// The block number at which this contribution can be withdrawn. withdraw_block: BlockNumber, + /// The contributor that will have `amount` deposited. contributor: AccountId, + /// Parachain ID that this crowdloan belongs to. + /// + /// Note that Bifrost ID 3356 is now 2030. para_id: ParaId, + /// Amount that was loaned to the crowdloan. amount: Balance, + /// The crowdloan pot account that will have `amount` removed. crowdloan_account: AccountId, }, - CrowdloanDeposit { + /// Reserve amount on a crowdloan pot account. + CrowdloanReserve { + /// The block number at which this deposit can be unreserved. unreserve_block: BlockNumber, + /// The account that has `amount` reserved. + /// + /// This is often the parachain manager or some multisig account from the parachain team + /// who initiated the crowdloan. depositor: AccountId, + /// Parachain ID that this crowdloan belongs to. + /// + /// Note that Bifrost ID 3356 is now 2030. para_id: ParaId, - fund_index: u32, + /// Amount that was reserved to create the crowdloan. + /// + /// Normally this is 500 DOT. TODO: Should sanity check. amount: Balance, }, } @@ -53,7 +82,7 @@ pub enum CrowdloanStage { Setup, LeaseReserve { last_key: Option }, CrowdloanContribution { last_key: Option }, - CrowdloanDeposit, + CrowdloanReserve, Finished, } @@ -95,7 +124,7 @@ impl PalletMigration for CrowdloanMigrator CrowdloanStage::Setup => { inner_key = CrowdloanStage::LeaseReserve { last_key: None }; - // Only thing to do here is to re-the bifrost crowdloan: https://polkadot.subsquare.io/referenda/524 + // Only thing to do here is to re-map the bifrost crowdloan: https://polkadot.subsquare.io/referenda/524 let leases = pallet_slots::Leases::::take(ParaId::from(2030)); if leases.is_empty() { defensive!("Bifrost fund maybe already ended, remove this"); @@ -103,8 +132,8 @@ impl PalletMigration for CrowdloanMigrator } // It would be better if we can re-map all contributions to the new para id, but - // that requires to iterate them all so we go the other way around; change the - // new leases to the old Bifrost Crowdloan. + // that requires to iterate them all, so we go the other way around; changing + // the leases to the old Bifrost Crowdloan. pallet_slots::Leases::::insert(ParaId::from(3356), leases); log::info!(target: LOG_TARGET, "Migrated Bifrost Leases from crowdloan 2030 to 3356"); @@ -118,16 +147,16 @@ impl PalletMigration for CrowdloanMigrator match iter.next() { Some((para_id, leases)) => { + inner_key = CrowdloanStage::LeaseReserve { last_key: Some(para_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 + // This seems to be fine, but i don't know why 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; }; @@ -135,8 +164,7 @@ impl PalletMigration for CrowdloanMigrator let amount: crate::BalanceOf = leases.iter().flatten().map(|(_acc, amount)| amount).max().cloned().unwrap_or_default().into(); if amount == 0u32.into() { - defensive_assert!(para_id < ParaId::from(2000), "Must be system chain"); - inner_key = CrowdloanStage::LeaseReserve { last_key: Some(para_id) }; + defensive_assert!(para_id < ParaId::from(2000), "Only system chains are allowed to have zero lease reserve"); continue; } @@ -144,7 +172,7 @@ impl PalletMigration for CrowdloanMigrator log::debug!(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) } + inner_key }, None => CrowdloanStage::CrowdloanContribution { last_key: None }, } @@ -158,7 +186,7 @@ impl PalletMigration for CrowdloanMigrator let (para_id, fund) = match funds_iter.next() { Some((para_id, fund)) => (para_id, fund), None => { - inner_key = CrowdloanStage::CrowdloanDeposit; + inner_key = CrowdloanStage::CrowdloanReserve; continue; }, }; @@ -174,29 +202,29 @@ impl PalletMigration for CrowdloanMigrator let leases = pallet_slots::Leases::::get(para_id); if leases.is_empty() { - defensive_assert!(fund.raised == 0u32.into(), "Fund should be empty"); + defensive_assert!(fund.raised == 0u32.into(), "Crowdloan should be empty if there are no leases"); inner_key = CrowdloanStage::CrowdloanContribution { last_key: Some(para_id) }; continue; } 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::debug!(target: LOG_TARGET, "Migrating out crowdloan contribution for para_id: {:?}, contributor: {:?}, amount: {:?}, withdraw_block: {:?}", ¶_id, &contributor, &amount, &withdraw_block); + // We directly remove so that we dont have to store a cursor: pallet_crowdloan::Pallet::::contribution_kill(fund.fund_index, &contributor); + + log::debug!(target: LOG_TARGET, "Migrating out crowdloan contribution for para_id: {:?}, contributor: {:?}, amount: {:?}, withdraw_block: {:?}", ¶_id, &contributor, &amount, &withdraw_block); + 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) } - }, + None => CrowdloanStage::CrowdloanContribution { last_key: Some(para_id) }, } }, - CrowdloanStage::CrowdloanDeposit => { + CrowdloanStage::CrowdloanReserve => { match pallet_crowdloan::Funds::::iter().next() { Some((para_id, fund)) => { - inner_key = CrowdloanStage::CrowdloanDeposit; + inner_key = CrowdloanStage::CrowdloanReserve; pallet_crowdloan::Funds::::take(para_id); let leases = pallet_slots::Leases::::get(para_id); @@ -207,15 +235,14 @@ impl PalletMigration for CrowdloanMigrator let unreserve_block = num_leases_to_ending_block::(leases.len() as u32); log::debug!(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 }); + + messages.push(RcCrowdloanMessage::CrowdloanReserve { unreserve_block, para_id, amount: fund.deposit.into(), depositor: fund.depositor }); inner_key }, None => CrowdloanStage::Finished, } }, - CrowdloanStage::Finished => { - break; - }, + CrowdloanStage::Finished => break, } } @@ -234,6 +261,7 @@ impl PalletMigration for CrowdloanMigrator } /// Calculate the lease ending block from the number of remaining leases (including the current). +// TODO tests fn num_leases_to_ending_block(num_leases: u32) -> BlockNumberFor { let now = frame_system::Pallet::::block_number(); let num_leases: BlockNumberFor = num_leases.into(); From f5e399587c5004494d5b631fac9b8f2fe3d797bc Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 17 Feb 2025 22:47:32 +0100 Subject: [PATCH 09/22] manual testing Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 2 ++ Cargo.toml | 1 + integration-tests/ahm/src/tests.rs | 3 +- pallets/ah-migrator/Cargo.toml | 2 ++ pallets/ah-migrator/src/crowdloan.rs | 46 ++++++++++++++++++++++++++++ pallets/ah-migrator/src/lib.rs | 3 ++ 6 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ae9799ddf7..6c40ed1496 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7646,6 +7646,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" name = "pallet-ah-migrator" version = "0.1.0" dependencies = [ + "chrono", "cumulus-primitives-core", "frame-benchmarking", "frame-support", @@ -7669,6 +7670,7 @@ dependencies = [ "pallet-scheduler", "pallet-staking", "pallet-state-trie-migration", + "pallet-timestamp", "pallet-treasury", "pallet-vesting", "parity-scale-codec", diff --git a/Cargo.toml b/Cargo.toml index 764768758c..f9258412c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ pallet-rc-migrator = { path = "pallets/rc-migrator", default-features = false } pallet-ah-migrator = { path = "pallets/ah-migrator", default-features = false } assert_matches = { version = "1.5.0" } approx = { version = "0.5.1" } +chrono = { version = "0.4.39", default-features = false } asset-hub-kusama-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-kusama" } asset-hub-kusama-runtime = { path = "system-parachains/asset-hubs/asset-hub-kusama" } asset-hub-polkadot-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-polkadot" } diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index fcd669c6dd..d74bff84f5 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -107,7 +107,8 @@ async fn account_migration_works() { } } - pallet_ah_migrator::preimage::PreimageMigrationCheck::::post_check(()); + pallet_ah_migrator::crowdloan::CrowdloanMigrationCheck::::post_check(); + //pallet_ah_migrator::preimage::PreimageMigrationCheck::::post_check(()); // NOTE that the DMP queue is probably not empty because the snapshot that we use contains // some overweight ones. }); diff --git a/pallets/ah-migrator/Cargo.toml b/pallets/ah-migrator/Cargo.toml index 7810747066..9330de26d9 100644 --- a/pallets/ah-migrator/Cargo.toml +++ b/pallets/ah-migrator/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] codec = { workspace = true, features = ["max-encoded-len"] } cumulus-primitives-core = { workspace = true } +chrono = { workspace = true } frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -32,6 +33,7 @@ pallet-staking = { workspace = true } pallet-state-trie-migration = { workspace = true } pallet-vesting = { workspace = true } pallet-treasury = { workspace = true } +pallet-timestamp = { workspace = true } polkadot-parachain-primitives = { workspace = true } polkadot-runtime-common = { workspace = true } runtime-parachains = { workspace = true } diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index 40720a7694..4382dda384 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -16,6 +16,7 @@ use crate::*; use frame_support::traits::tokens::Preservation; use pallet_rc_migrator::crowdloan::RcCrowdloanMessage; +use chrono::TimeZone; impl Pallet { pub fn do_receive_crowdloan_messages( @@ -106,6 +107,7 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { + ensure!(block > T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); let balance = RcLeaseReserve::::take((block, &depositor, para_id)) .ok_or(Error::::NoLeaseReserve)?; @@ -123,6 +125,7 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { + ensure!(block > T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); let (pot, contribution) = RcCrowdloanContribution::::take((block, &depositor, para_id)) .ok_or(Error::::NoCrowdloanContribution)?; @@ -154,6 +157,7 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { + ensure!(block > T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); let amount = RcCrowdloanReserve::::take((block, &depositor, para_id)) .ok_or(Error::::NoCrowdloanReserve)?; @@ -170,3 +174,45 @@ impl Pallet { Ok(()) } } + +pub struct CrowdloanMigrationCheck(pub PhantomData); + +#[cfg(feature = "std")] +impl CrowdloanMigrationCheck + where +BlockNumberFor: Into { + pub fn post_check() { + println!("Lease reserve info"); + let lease_reserves = RcLeaseReserve::::iter().collect::>(); + for ((unlock_block, who, para_id), value) in &lease_reserves { + println!("lr [{unlock_block}] {para_id} {who}: {} ({})", value / 10_000_000_000, Self::block_to_date(*unlock_block)); + } + + let total_reserved = lease_reserves.iter().map(|((_, _, _), value)| value).sum::(); + println!("Num lease reserves: {}, total reserved amount: {}", lease_reserves.len(), total_reserved / 10_000_000_000); + + println!("Crowdloan reserve info"); + let crowdloan_reserves = RcCrowdloanReserve::::iter().collect::>(); + for ((unlock_block, who, para_id), value) in &crowdloan_reserves { + println!("cr [{unlock_block}] {para_id} {who}: {} ({})", value / 10_000_000_000, Self::block_to_date(*unlock_block)); + } + + let total_reserved = crowdloan_reserves.iter().map(|((_, _, _), value)| value).sum::(); + println!("Num crowdloan reserves: {}, total reserved amount: {}", crowdloan_reserves.len(), total_reserved / 10_000_000_000); + } + + #[cfg(feature = "std")] + fn block_to_date(block: BlockNumberFor) -> chrono::DateTime { + let anchor_block: u64 = T::RcBlockNumberProvider::current_block_number().into(); + // We are using the time from AH here, not relay. But the snapshots are taken together. + let anchor_timestamp: u64 = pallet_timestamp::Now::::get().into(); + + let block_diff: u64 = (block.into() - anchor_block).into(); + let add_time_ms: i64 = (block_diff * 6_000) as i64; + + // convert anchor_timestamp to chrono timestamp + let anchor_timestamp = chrono::Utc.timestamp_millis(anchor_timestamp as i64); + let block_timestamp = anchor_timestamp + chrono::Duration::milliseconds(add_time_ms); + block_timestamp + } +} diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index ee5c906db9..b49e53bac7 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -137,6 +137,7 @@ pub mod pallet { + pallet_bounties::Config + pallet_treasury::Config + pallet_asset_rate::Config + + pallet_timestamp::Config // Needed for testing { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -286,6 +287,8 @@ pub mod pallet { NoCrowdloanReserve, /// Failed to withdraw crowdloan contribution. FailedToWithdrawCrowdloanContribution, + /// Block number is not yet reached. + NotYet, } #[pallet::event] From c3d754498a989cf2e408a8a699d3fbe6ad7aa78b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 19 Feb 2025 17:54:52 +0100 Subject: [PATCH 10/22] Block must be in the past Signed-off-by: Oliver Tale-Yazdi --- pallets/ah-migrator/src/crowdloan.rs | 42 ++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index 4382dda384..25f54e9c49 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -14,9 +14,9 @@ // along with Polkadot. If not, see . use crate::*; +use chrono::TimeZone; use frame_support::traits::tokens::Preservation; use pallet_rc_migrator::crowdloan::RcCrowdloanMessage; -use chrono::TimeZone; impl Pallet { pub fn do_receive_crowdloan_messages( @@ -107,7 +107,7 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { - ensure!(block > T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); + ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); let balance = RcLeaseReserve::::take((block, &depositor, para_id)) .ok_or(Error::::NoLeaseReserve)?; @@ -125,7 +125,7 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { - ensure!(block > T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); + ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); let (pot, contribution) = RcCrowdloanContribution::::take((block, &depositor, para_id)) .ok_or(Error::::NoCrowdloanContribution)?; @@ -157,7 +157,7 @@ impl Pallet { depositor: T::AccountId, para_id: ParaId, ) -> Result<(), Error> { - ensure!(block > T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); + ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); let amount = RcCrowdloanReserve::::take((block, &depositor, para_id)) .ok_or(Error::::NoCrowdloanReserve)?; @@ -179,26 +179,44 @@ pub struct CrowdloanMigrationCheck(pub PhantomData); #[cfg(feature = "std")] impl CrowdloanMigrationCheck - where -BlockNumberFor: Into { +where + BlockNumberFor: Into, +{ pub fn post_check() { println!("Lease reserve info"); let lease_reserves = RcLeaseReserve::::iter().collect::>(); for ((unlock_block, who, para_id), value) in &lease_reserves { - println!("lr [{unlock_block}] {para_id} {who}: {} ({})", value / 10_000_000_000, Self::block_to_date(*unlock_block)); + println!( + "lr [{unlock_block}] {para_id} {who}: {} ({})", + value / 10_000_000_000, + Self::block_to_date(*unlock_block) + ); } let total_reserved = lease_reserves.iter().map(|((_, _, _), value)| value).sum::(); - println!("Num lease reserves: {}, total reserved amount: {}", lease_reserves.len(), total_reserved / 10_000_000_000); + println!( + "Num lease reserves: {}, total reserved amount: {}", + lease_reserves.len(), + total_reserved / 10_000_000_000 + ); println!("Crowdloan reserve info"); let crowdloan_reserves = RcCrowdloanReserve::::iter().collect::>(); for ((unlock_block, who, para_id), value) in &crowdloan_reserves { - println!("cr [{unlock_block}] {para_id} {who}: {} ({})", value / 10_000_000_000, Self::block_to_date(*unlock_block)); + println!( + "cr [{unlock_block}] {para_id} {who}: {} ({})", + value / 10_000_000_000, + Self::block_to_date(*unlock_block) + ); } - let total_reserved = crowdloan_reserves.iter().map(|((_, _, _), value)| value).sum::(); - println!("Num crowdloan reserves: {}, total reserved amount: {}", crowdloan_reserves.len(), total_reserved / 10_000_000_000); + let total_reserved = + crowdloan_reserves.iter().map(|((_, _, _), value)| value).sum::(); + println!( + "Num crowdloan reserves: {}, total reserved amount: {}", + crowdloan_reserves.len(), + total_reserved / 10_000_000_000 + ); } #[cfg(feature = "std")] @@ -209,7 +227,7 @@ BlockNumberFor: Into { let block_diff: u64 = (block.into() - anchor_block).into(); let add_time_ms: i64 = (block_diff * 6_000) as i64; - + // convert anchor_timestamp to chrono timestamp let anchor_timestamp = chrono::Utc.timestamp_millis(anchor_timestamp as i64); let block_timestamp = anchor_timestamp + chrono::Duration::milliseconds(add_time_ms); From 8c9787dc6d310dc05893ddd240e5872ad77cdbe3 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 11:53:58 +0100 Subject: [PATCH 11/22] Add AHM Ops pallet Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 46 +++ Cargo.toml | 2 + pallets/ah-migrator/Cargo.toml | 10 + pallets/ah-migrator/src/crowdloan.rs | 124 ++----- pallets/ah-migrator/src/lib.rs | 133 +------ pallets/ah-migrator/src/staking/nom_pools.rs | 2 +- pallets/ah-ops/Cargo.toml | 145 ++++++++ pallets/ah-ops/src/lib.rs | 343 ++++++++++++++++++ pallets/rc-migrator/src/crowdloan.rs | 3 +- pallets/rc-migrator/src/types.rs | 2 +- .../asset-hubs/asset-hub-polkadot/Cargo.toml | 4 + .../asset-hubs/asset-hub-polkadot/src/lib.rs | 11 +- 12 files changed, 592 insertions(+), 233 deletions(-) create mode 100644 pallets/ah-ops/Cargo.toml create mode 100644 pallets/ah-ops/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6c40ed1496..223ac09871 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -784,6 +784,7 @@ dependencies = [ "kusama-runtime-constants", "log", "pallet-ah-migrator", + "pallet-ah-ops", "pallet-asset-conversion", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", @@ -7645,6 +7646,51 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pallet-ah-migrator" version = "0.1.0" +dependencies = [ + "chrono", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "log", + "pallet-ah-ops", + "pallet-asset-rate", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-conviction-voting", + "pallet-fast-unstake", + "pallet-indices", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-preimage", + "pallet-proxy", + "pallet-rc-migrator", + "pallet-referenda", + "pallet-scheduler", + "pallet-staking", + "pallet-state-trie-migration", + "pallet-timestamp", + "pallet-treasury", + "pallet-vesting", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "scale-info", + "serde", + "sp-application-crypto 38.0.0", + "sp-core 34.0.0", + "sp-io 38.0.0", + "sp-runtime 39.0.5", + "sp-std", +] + +[[package]] +name = "pallet-ah-ops" +version = "0.1.0" dependencies = [ "chrono", "cumulus-primitives-core", diff --git a/Cargo.toml b/Cargo.toml index f9258412c2..a8d94acc18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ license = "GPL-3.0-only" # TODO Pallet { @@ -52,13 +51,16 @@ impl Pallet { 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(( + defensive_assert!(!pallet_ah_ops::RcLeaseReserve::::contains_key(( unreserve_block, - &account, - para_id + para_id, + &account ))); - RcLeaseReserve::::insert((unreserve_block, account, para_id), amount); + pallet_ah_ops::RcLeaseReserve::::insert( + (unreserve_block, para_id, &account), + amount, + ); }, RcCrowdloanMessage::CrowdloanContribution { withdraw_block, @@ -68,14 +70,14 @@ impl Pallet { 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(( + defensive_assert!(!pallet_ah_ops::RcCrowdloanContribution::::contains_key(( withdraw_block, - &contributor, - para_id + para_id, + &contributor ))); - RcCrowdloanContribution::::insert( - (withdraw_block, contributor, para_id), + pallet_ah_ops::RcCrowdloanContribution::::insert( + (withdraw_block, para_id, &contributor), (crowdloan_account, amount), ); }, @@ -86,13 +88,16 @@ impl Pallet { depositor, } => { log::info!(target: LOG_TARGET, "Integrating crowdloan reserve for para_id: {:?}, amount: {:?}, depositor: {:?}", ¶_id, &amount, &depositor); - defensive_assert!(!RcCrowdloanReserve::::contains_key(( + defensive_assert!(!pallet_ah_ops::RcCrowdloanReserve::::contains_key(( unreserve_block, - &depositor, - para_id + para_id, + &depositor ))); - RcCrowdloanReserve::::insert((unreserve_block, depositor, para_id), amount); + pallet_ah_ops::RcCrowdloanReserve::::insert( + (unreserve_block, para_id, &depositor), + amount, + ); }, } @@ -101,79 +106,7 @@ impl Pallet { } // extrinsic code -impl Pallet { - pub fn do_unreserve_lease_deposit( - block: BlockNumberFor, - depositor: T::AccountId, - para_id: ParaId, - ) -> Result<(), Error> { - ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); - let balance = RcLeaseReserve::::take((block, &depositor, para_id)) - .ok_or(Error::::NoLeaseReserve)?; - - let remaining = ::Currency::unreserve(&depositor, balance); - if remaining > 0 { - defensive!("Should be able to unreserve all"); - Self::deposit_event(Event::LeaseUnreserveRemaining { depositor, remaining, para_id }); - } - - Ok(()) - } - - pub fn do_withdraw_crowdloan_contribution( - block: BlockNumberFor, - depositor: T::AccountId, - para_id: ParaId, - ) -> Result<(), Error> { - ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); - let (pot, contribution) = RcCrowdloanContribution::::take((block, &depositor, para_id)) - .ok_or(Error::::NoCrowdloanContribution)?; - - // Maybe this is the first one to withdraw and we need to unreserve it from the pot - match Self::do_unreserve_lease_deposit(block, pot.clone(), para_id) { - Ok(()) => (), - Err(Error::::NoLeaseReserve) => (), // fine - Err(e) => return Err(e), - } - - // Ideally this does not fail. But if it does, then we keep it for manual inspection. - let transferred = ::Currency::transfer( - &pot, - &depositor, - contribution, - Preservation::Preserve, - ) - .defensive() - .map_err(|_| Error::::FailedToWithdrawCrowdloanContribution)?; - defensive_assert!(transferred == contribution); - // Need to reactivate since we deactivated it here https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L793 - ::Currency::reactivate(transferred); - - Ok(()) - } - - pub fn do_unreserve_crowdloan_reserve( - block: BlockNumberFor, - depositor: T::AccountId, - para_id: ParaId, - ) -> Result<(), Error> { - ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); - let amount = RcCrowdloanReserve::::take((block, &depositor, para_id)) - .ok_or(Error::::NoCrowdloanReserve)?; - - let remaining = ::Currency::unreserve(&depositor, amount); - if remaining > 0 { - defensive!("Should be able to unreserve all"); - Self::deposit_event(Event::CrowdloanUnreserveRemaining { - depositor, - remaining, - para_id, - }); - } - - Ok(()) - } -} +impl Pallet {} pub struct CrowdloanMigrationCheck(pub PhantomData); @@ -184,10 +117,10 @@ where { pub fn post_check() { println!("Lease reserve info"); - let lease_reserves = RcLeaseReserve::::iter().collect::>(); - for ((unlock_block, who, para_id), value) in &lease_reserves { + let lease_reserves = pallet_ah_ops::RcLeaseReserve::::iter().collect::>(); + for ((unlock_block, para_id, who), value) in &lease_reserves { println!( - "lr [{unlock_block}] {para_id} {who}: {} ({})", + "Lease Reserve [{unlock_block}] {para_id} {who}: {} ({})", value / 10_000_000_000, Self::block_to_date(*unlock_block) ); @@ -201,10 +134,10 @@ where ); println!("Crowdloan reserve info"); - let crowdloan_reserves = RcCrowdloanReserve::::iter().collect::>(); - for ((unlock_block, who, para_id), value) in &crowdloan_reserves { + let crowdloan_reserves = pallet_ah_ops::RcCrowdloanReserve::::iter().collect::>(); + for ((unlock_block, para_id, who), value) in &crowdloan_reserves { println!( - "cr [{unlock_block}] {para_id} {who}: {} ({})", + "Crowdloan Reserve [{unlock_block}] {para_id} {who}: {} ({})", value / 10_000_000_000, Self::block_to_date(*unlock_block) ); @@ -221,7 +154,8 @@ where #[cfg(feature = "std")] fn block_to_date(block: BlockNumberFor) -> chrono::DateTime { - let anchor_block: u64 = T::RcBlockNumberProvider::current_block_number().into(); + let anchor_block: u64 = + ::RcBlockNumberProvider::current_block_number().into(); // We are using the time from AH here, not relay. But the snapshots are taken together. let anchor_timestamp: u64 = pallet_timestamp::Now::::get().into(); @@ -229,7 +163,7 @@ where let add_time_ms: i64 = (block_diff * 6_000) as i64; // convert anchor_timestamp to chrono timestamp - let anchor_timestamp = chrono::Utc.timestamp_millis(anchor_timestamp as i64); + let anchor_timestamp = chrono::Utc.timestamp_millis_opt(anchor_timestamp as i64).unwrap(); let block_timestamp = anchor_timestamp + chrono::Duration::milliseconds(add_time_ms); block_timestamp } diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index b49e53bac7..6d20fc3f08 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -138,6 +138,7 @@ pub mod pallet { + pallet_treasury::Config + pallet_asset_rate::Config + pallet_timestamp::Config // Needed for testing + + pallet_ah_ops::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -190,76 +191,6 @@ pub mod pallet { pub type RcAccounts = StorageMap<_, Twox64Concat, T::AccountId, RcAccountFor, OptionQuery>; - /// Amount of balance that was reserved for winning a lease auction. - /// - /// `unreserve_lease_deposit` can be permissionlessly called once the block number passed to - /// unreserve the deposit. It is implicitly called by `withdraw_crowdloan_contribution`. - /// - /// The account here can either be a crowdloan account or a solo bidder. If it is a crowdloan - /// account, then the summed up contributions for it in the contributions map will equate the - /// reserved balance here. - /// - /// The keys are as follows: - /// - Block number after which the deposit can be unreserved. - /// - The account that will have the balance unreserved. - /// - The para_id of the lease slot. - /// - The balance to be unreserved. - #[pallet::storage] - pub type RcLeaseReserve = StorageNMap< - _, - ( - NMapKey>, - NMapKey, - NMapKey, - ), - BalanceOf, - OptionQuery, - >; - - /// Amount of balance that a contributor made towards a crowdloan. - /// - /// `withdraw_crowdloan_contribution` can be permissionlessly called once the block number - /// passed to unlock the balance for a specific account. - /// - /// The keys are as follows: - /// - Block number after which the balance can be unlocked. - /// - The account that made the contribution. - /// - The para_id of the crowdloan. - /// - /// The value is (fund_pot, balance). The contribution pot is the second key in the - /// `RcCrowdloanContribution` storage. - #[pallet::storage] - pub type RcCrowdloanContribution = StorageNMap< - _, - ( - NMapKey>, - NMapKey, - NMapKey, - ), - (T::AccountId, BalanceOf), - 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. @@ -689,69 +620,7 @@ pub mod pallet { Self::do_receive_asset_rates(rates).map_err(Into::into) } - /// Unreserve the deposit that was taken for creating a crowdloan. - /// - /// This can be called by any signed origin. It unreserves the lease deposit on the account - /// that won the lease auction. It can be unreserved once all leases expired. Note that it - /// will be called automatically from `withdraw_crowdloan_contribution` for the matching - /// crowdloan account. - /// - /// Solo bidder accounts that won lease auctions can use this to unreserve their amount. #[pallet::call_index(19)] - pub fn unreserve_lease_deposit( - origin: OriginFor, - block: BlockNumberFor, - depositor: Option, - para_id: ParaId, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - let depositor = depositor.unwrap_or(sender); - - Self::do_unreserve_lease_deposit(block, depositor, para_id).map_err(Into::into) - } - - /// Withdraw the contribution of a finished crowdloan. - /// - /// A crowdloan contribution can be withdrawn if either: - /// - The crowdloan failed to in an auction and timed out - /// - Won an auction and all leases expired - /// - /// Can be called by any signed origin. - #[pallet::call_index(20)] - pub fn withdraw_crowdloan_contribution( - origin: OriginFor, - block: BlockNumberFor, - depositor: Option, - para_id: ParaId, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - let depositor = depositor.unwrap_or(sender); - - Self::do_withdraw_crowdloan_contribution(block, depositor, para_id).map_err(Into::into) - } - - /// Unreserve the deposit that was taken for creating a crowdloan. - /// - /// This can be called once: - /// - The crowdloan failed to win an auction and timed out - /// - Won an auction and all leases expired - /// - /// Can be called by any signed origin. The condition that all contributions are withdrawn - /// is not checked anymore since all withdraw and unreserve functions are permissionless. - #[pallet::call_index(21)] - pub fn unreserve_crowdloan_reserve( - origin: OriginFor, - block: BlockNumberFor, - depositor: Option, - para_id: ParaId, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - let depositor = depositor.unwrap_or(sender); - - Self::do_unreserve_crowdloan_reserve(block, depositor, para_id).map_err(Into::into) - } - - #[pallet::call_index(22)] pub fn receive_crowdloan_messages( origin: OriginFor, messages: Vec>, diff --git a/pallets/ah-migrator/src/staking/nom_pools.rs b/pallets/ah-migrator/src/staking/nom_pools.rs index 088e57db44..2c830a86f6 100644 --- a/pallets/ah-migrator/src/staking/nom_pools.rs +++ b/pallets/ah-migrator/src/staking/nom_pools.rs @@ -125,7 +125,7 @@ impl Pallet { /// - RC time point: 150 /// - Result: 75 + (150 - 100) / 2 = 100 pub fn rc_to_ah_timepoint(rc_timepoint: BlockNumberFor) -> BlockNumberFor { - let rc_now = T::RcBlockNumberProvider::current_block_number(); + let rc_now = ::RcBlockNumberProvider::current_block_number(); let ah_now = frame_system::Pallet::::block_number(); if let Some(rc_since) = rc_now.checked_sub(&rc_timepoint) { diff --git a/pallets/ah-ops/Cargo.toml b/pallets/ah-ops/Cargo.toml new file mode 100644 index 0000000000..5b6d0cf204 --- /dev/null +++ b/pallets/ah-ops/Cargo.toml @@ -0,0 +1,145 @@ +[package] +name = "pallet-ah-ops" +description = "Operations cleanup pallet for the post-migration Asset Hub" +license = "Apache-2.0" +version = "0.1.0" +edition.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +codec = { workspace = true, features = ["max-encoded-len"] } +cumulus-primitives-core = { workspace = true } +chrono = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +pallet-asset-rate = { workspace = true } +pallet-balances = { workspace = true } +pallet-bags-list = { workspace = true } +pallet-bounties = { workspace = true } +pallet-conviction-voting = { workspace = true } +pallet-fast-unstake = { workspace = true } +pallet-multisig = { workspace = true } +pallet-indices = { workspace = true } +pallet-nomination-pools = { workspace = true } +pallet-preimage = { workspace = true } +pallet-proxy = { workspace = true } +pallet-rc-migrator = { workspace = true } +pallet-referenda = { workspace = true } +pallet-scheduler = { workspace = true } +pallet-staking = { workspace = true } +pallet-state-trie-migration = { workspace = true } +pallet-vesting = { workspace = true } +pallet-treasury = { workspace = true } +pallet-timestamp = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-runtime-common = { workspace = true } +runtime-parachains = { workspace = true } +scale-info = { workspace = true, features = ["derive"] } +serde = { features = ["derive"], optional = true, workspace = true } +sp-application-crypto = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +hex-literal = { workspace = true } +hex = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "hex/std", + "log/std", + "pallet-asset-rate/std", + "pallet-bags-list/std", + "pallet-balances/std", + "pallet-bounties/std", + "pallet-conviction-voting/std", + "pallet-fast-unstake/std", + "pallet-indices/std", + "pallet-multisig/std", + "pallet-nomination-pools/std", + "pallet-preimage/std", + "pallet-proxy/std", + "pallet-rc-migrator/std", + "pallet-referenda/std", + "pallet-scheduler/std", + "pallet-staking/std", + "pallet-state-trie-migration/std", + "pallet-treasury/std", + "pallet-vesting/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "runtime-parachains/std", + "scale-info/std", + "serde", + "sp-application-crypto/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "chrono/std", + "cumulus-primitives-core/std", + "pallet-timestamp/std" +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-asset-rate/runtime-benchmarks", + "pallet-bags-list/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-bounties/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-fast-unstake/runtime-benchmarks", + "pallet-indices/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-nomination-pools/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-rc-migrator/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-staking/runtime-benchmarks", + "pallet-state-trie-migration/runtime-benchmarks", + "pallet-treasury/runtime-benchmarks", + "pallet-vesting/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "runtime-parachains/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks" +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-asset-rate/try-runtime", + "pallet-bags-list/try-runtime", + "pallet-balances/try-runtime", + "pallet-bounties/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-fast-unstake/try-runtime", + "pallet-indices/try-runtime", + "pallet-multisig/try-runtime", + "pallet-nomination-pools/try-runtime", + "pallet-preimage/try-runtime", + "pallet-proxy/try-runtime", + "pallet-rc-migrator/try-runtime", + "pallet-referenda/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-staking/try-runtime", + "pallet-state-trie-migration/try-runtime", + "pallet-treasury/try-runtime", + "pallet-vesting/try-runtime", + "polkadot-runtime-common/try-runtime", + "runtime-parachains/try-runtime", + "sp-runtime/try-runtime", + "pallet-timestamp/try-runtime" +] diff --git a/pallets/ah-ops/src/lib.rs b/pallets/ah-ops/src/lib.rs new file mode 100644 index 0000000000..0dd9e54d39 --- /dev/null +++ b/pallets/ah-ops/src/lib.rs @@ -0,0 +1,343 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The operational pallet for the Asset Hub, designed to manage and facilitate the migration of +//! subsystems such as Governance, Staking, Balances from the Relay Chain to the Asset Hub. This +//! pallet works alongside its counterpart, `pallet_rc_migrator`, which handles migration +//! processes on the Relay Chain side. +//! +//! This pallet is responsible for controlling the initiation, progression, and completion of the +//! migration process, including managing its various stages and transferring the necessary data. +//! The pallet directly accesses the storage of other pallets for read/write operations while +//! maintaining compatibility with their existing APIs. +//! +//! To simplify development and avoid the need to edit the original pallets, this pallet may +//! duplicate private items such as storage entries from the original pallets. This ensures that the +//! migration logic can be implemented without altering the original implementations. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +use cumulus_primitives_core::ParaId; +use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{InspectFreeze, Mutate, MutateFreeze, MutateHold, Unbalanced}, + tokens::Preservation, + Defensive, LockableCurrency, ReservableCurrency, + }, +}; +use frame_system::pallet_prelude::*; +use pallet_balances::AccountData; +use sp_runtime::{traits::BlockNumberProvider, AccountId32}; +use sp_std::prelude::*; + +/// The log target of this pallet. +pub const LOG_TARGET: &str = "runtime::ah-migrator"; + +pub type BalanceOf = ::Balance; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: + frame_system::Config, AccountId = AccountId32> + + pallet_balances::Config + + pallet_timestamp::Config // Needed for testing + { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Native asset type. + type Currency: Mutate + + MutateHold + + InspectFreeze + + MutateFreeze + + Unbalanced + + ReservableCurrency + + LockableCurrency; + + /// Access the block number of the Relay Chain. + type RcBlockNumberProvider: BlockNumberProvider>; + } + + /// Amount of balance that was reserved for winning a lease auction. + /// + /// `unreserve_lease_deposit` can be permissionlessly called once the block number passed to + /// unreserve the deposit. It is implicitly called by `withdraw_crowdloan_contribution`. + /// + /// The account here can either be a crowdloan account or a solo bidder. If it is a crowdloan + /// account, then the summed up contributions for it in the contributions map will equate the + /// reserved balance here. + /// + /// The keys are as follows: + /// - Block number after which the deposit can be unreserved. + /// - The para_id of the lease slot. + /// - The account that will have the balance unreserved. + /// - The balance to be unreserved. + #[pallet::storage] + pub type RcLeaseReserve = StorageNMap< + _, + ( + NMapKey>, + NMapKey, + NMapKey, + ), + BalanceOf, + OptionQuery, + >; + + /// Amount of balance that a contributor made towards a crowdloan. + /// + /// `withdraw_crowdloan_contribution` can be permissionlessly called once the block number + /// passed to unlock the balance for a specific account. + /// + /// The keys are as follows: + /// - Block number after which the balance can be unlocked. + /// - The para_id of the crowdloan. + /// - The account that made the contribution. + /// + /// The value is (fund_pot, balance). The contribution pot is the second key in the + /// `RcCrowdloanContribution` storage. + #[pallet::storage] + pub type RcCrowdloanContribution = StorageNMap< + _, + ( + NMapKey>, + NMapKey, + NMapKey, + ), + (T::AccountId, BalanceOf), + 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 + /// - The para_id of the crowdloan + /// - The account that will have the balance unreserved + #[pallet::storage] + pub type RcCrowdloanReserve = StorageNMap< + _, + ( + NMapKey>, + NMapKey, + NMapKey, + ), + BalanceOf, + OptionQuery, + >; + + #[pallet::error] + pub enum Error { + TODO, + /// Either no lease deposit or already unreserved. + NoLeaseReserve, + /// Either no crowdloan contribution or already withdrawn. + NoCrowdloanContribution, + /// Either no crowdloan reserve or already unreserved. + NoCrowdloanReserve, + /// Failed to withdraw crowdloan contribution. + FailedToWithdrawCrowdloanContribution, + /// Block number is not yet reached. + NotYet, + /// Not all contributions are withdrawn. + ContributionsRemaining, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// Some lease reserve could not be unreserved and needs manual cleanup. + LeaseUnreserveRemaining { + depositor: T::AccountId, + para_id: ParaId, + remaining: BalanceOf, + }, + + /// Some amount for a crowdloan reserve could not be unreserved and needs manual cleanup. + CrowdloanUnreserveRemaining { + depositor: T::AccountId, + para_id: ParaId, + remaining: BalanceOf, + }, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + /// Unreserve the deposit that was taken for creating a crowdloan. + /// + /// This can be called by any signed origin. It unreserves the lease deposit on the account + /// that won the lease auction. It can be unreserved once all leases expired. Note that it + /// will be called automatically from `withdraw_crowdloan_contribution` for the matching + /// crowdloan account. + /// + /// Solo bidder accounts that won lease auctions can use this to unreserve their amount. + #[pallet::call_index(0)] + pub fn unreserve_lease_deposit( + origin: OriginFor, + block: BlockNumberFor, + depositor: Option, + para_id: ParaId, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let depositor = depositor.unwrap_or(sender); + + Self::do_unreserve_lease_deposit(block, depositor, para_id).map_err(Into::into) + } + + /// Withdraw the contribution of a finished crowdloan. + /// + /// A crowdloan contribution can be withdrawn if either: + /// - The crowdloan failed to in an auction and timed out + /// - Won an auction and all leases expired + /// + /// Can be called by any signed origin. + #[pallet::call_index(1)] + pub fn withdraw_crowdloan_contribution( + origin: OriginFor, + block: BlockNumberFor, + depositor: Option, + para_id: ParaId, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let depositor = depositor.unwrap_or(sender); + + Self::do_withdraw_crowdloan_contribution(block, depositor, para_id).map_err(Into::into) + } + + /// Unreserve the deposit that was taken for creating a crowdloan. + /// + /// This can be called once either: + /// - The crowdloan failed to win an auction and timed out + /// - Won an auction, all leases expired and all contributions are withdrawn + /// + /// Can be called by any signed origin. The condition that all contributions are withdrawn + /// is in place since the reserve acts as a storage deposit. + #[pallet::call_index(2)] + pub fn unreserve_crowdloan_reserve( + origin: OriginFor, + block: BlockNumberFor, + depositor: Option, + para_id: ParaId, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let depositor = depositor.unwrap_or(sender); + + Self::do_unreserve_crowdloan_reserve(block, depositor, para_id).map_err(Into::into) + } + } + + impl Pallet { + pub fn do_unreserve_lease_deposit( + block: BlockNumberFor, + depositor: T::AccountId, + para_id: ParaId, + ) -> Result<(), Error> { + ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); + let balance = RcLeaseReserve::::take((block, para_id, &depositor)) + .ok_or(Error::::NoLeaseReserve)?; + + let remaining = ::Currency::unreserve(&depositor, balance); + if remaining > 0 { + defensive!("Should be able to unreserve all"); + Self::deposit_event(Event::LeaseUnreserveRemaining { + depositor, + remaining, + para_id, + }); + } + + Ok(()) + } + + pub fn do_withdraw_crowdloan_contribution( + block: BlockNumberFor, + depositor: T::AccountId, + para_id: ParaId, + ) -> Result<(), Error> { + ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); + let (pot, contribution) = + RcCrowdloanContribution::::take((block, para_id, &depositor)) + .ok_or(Error::::NoCrowdloanContribution)?; + + // Maybe this is the first one to withdraw and we need to unreserve it from the pot + match Self::do_unreserve_lease_deposit(block, pot.clone(), para_id) { + Ok(()) => (), + Err(Error::::NoLeaseReserve) => (), // fine + Err(e) => return Err(e), + } + + // Ideally this does not fail. But if it does, then we keep it for manual inspection. + let transferred = ::Currency::transfer( + &pot, + &depositor, + contribution, + Preservation::Preserve, + ) + .defensive() + .map_err(|_| Error::::FailedToWithdrawCrowdloanContribution)?; + defensive_assert!(transferred == contribution); + // Need to reactivate since we deactivated it here https://github.com/paritytech/polkadot-sdk/blob/04847d515ef56da4d0801c9b89a4241dfa827b33/polkadot/runtime/common/src/crowdloan/mod.rs#L793 + ::Currency::reactivate(transferred); + + Ok(()) + } + + pub fn do_unreserve_crowdloan_reserve( + block: BlockNumberFor, + depositor: T::AccountId, + para_id: ParaId, + ) -> Result<(), Error> { + ensure!(block <= T::RcBlockNumberProvider::current_block_number(), Error::::NotYet); + ensure!( + Self::contributions_withdrawn(block, para_id), + Error::::ContributionsRemaining + ); + let amount = RcCrowdloanReserve::::take((block, para_id, &depositor)) + .ok_or(Error::::NoCrowdloanReserve)?; + + let remaining = ::Currency::unreserve(&depositor, amount); + if remaining > 0 { + defensive!("Should be able to unreserve all"); + Self::deposit_event(Event::CrowdloanUnreserveRemaining { + depositor, + remaining, + para_id, + }); + } + + Ok(()) + } + + // TODO Test this + fn contributions_withdrawn(block: BlockNumberFor, para_id: ParaId) -> bool { + let mut contrib_iter = RcCrowdloanContribution::::iter_prefix((block, para_id)); + contrib_iter.next().is_none() + } + } +} diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index f55e1b5ed0..ce354602c1 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -195,6 +195,7 @@ impl PalletMigration for CrowdloanMigrator match contributions_iter.next() { Some((contributor, (amount, memo))) => { + inner_key = CrowdloanStage::CrowdloanContribution { last_key: Some(para_id) }; // 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()); @@ -203,8 +204,6 @@ impl PalletMigration for CrowdloanMigrator let leases = pallet_slots::Leases::::get(para_id); if leases.is_empty() { defensive_assert!(fund.raised == 0u32.into(), "Crowdloan should be empty if there are no leases"); - inner_key = CrowdloanStage::CrowdloanContribution { last_key: Some(para_id) }; - continue; } let crowdloan_account = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); diff --git a/pallets/rc-migrator/src/types.rs b/pallets/rc-migrator/src/types.rs index 09084fce8b..4c37abc563 100644 --- a/pallets/rc-migrator/src/types.rs +++ b/pallets/rc-migrator/src/types.rs @@ -76,7 +76,7 @@ pub enum AhMigratorCall { ReceiveBountiesMessages { messages: Vec> }, #[codec(index = 18)] ReceiveAssetRates { asset_rates: Vec<(::AssetKind, FixedU128)> }, - #[codec(index = 22)] + #[codec(index = 19)] ReceiveCrowdloanMessages { messages: Vec> }, } diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml index 02d0ea0f0b..926092a8b0 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml @@ -10,6 +10,7 @@ version.workspace = true [dependencies] pallet-ah-migrator = { workspace = true } +pallet-ah-ops = { workspace = true } codec = { features = ["derive", "max-encoded-len"], workspace = true } hex-literal = { optional = true, workspace = true } @@ -186,6 +187,7 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", + "pallet-ah-ops/runtime-benchmarks" ] try-runtime = [ "pallet-ah-migrator/try-runtime", @@ -235,6 +237,7 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", "system-parachains-common/try-runtime", + "pallet-ah-ops/try-runtime" ] std = [ "pallet-ah-migrator/std", @@ -334,6 +337,7 @@ std = [ "xcm-executor/std", "xcm-runtime-apis/std", "xcm/std", + "pallet-ah-ops/std" ] # Enable metadata hash generation at compile time for the `CheckMetadataHash` extension. diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs index a0704070a8..f516de142e 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs @@ -1118,6 +1118,12 @@ impl pallet_claims::Config for Runtime { type WeightInfo = pallet_claims::TestWeightInfo; // TODOweights::polkadot_runtime_common_claims::WeightInfo; } +impl pallet_ah_ops::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RcBlockNumberProvider = RelaychainDataProvider; +} + impl pallet_ah_migrator::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -1184,7 +1190,7 @@ construct_runtime!( PoolAssets: pallet_assets:: = 54, AssetConversion: pallet_asset_conversion = 55, - // OpenGov stuff. + // OpenGov stuff Treasury: pallet_treasury = 60, ConvictionVoting: pallet_conviction_voting = 61, Referenda: pallet_referenda = 62, @@ -1199,7 +1205,8 @@ construct_runtime!( FastUnstake: pallet_fast_unstake = 71, VoterList: pallet_bags_list:: = 72, - // Asset Hub Migrator + // Asset Hub Migration in the 250s + AhOps: pallet_ah_ops = 254, AhMigrator: pallet_ah_migrator = 255, } ); From 9f8dc149002b1c47f29aadd698742c3f539403fb Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 14:05:33 +0100 Subject: [PATCH 12/22] fixes Signed-off-by: Oliver Tale-Yazdi --- integration-tests/ahm/src/tests.rs | 38 +++++++++++++++++++++++++++- pallets/rc-migrator/src/crowdloan.rs | 37 ++++++++++++++++++++++----- pallets/rc-migrator/src/lib.rs | 3 +-- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index d74bff84f5..f61d55bb5b 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -34,8 +34,10 @@ use asset_hub_polkadot_runtime::Runtime as AssetHub; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::traits::*; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_rc_migrator::{types::PalletMigrationChecks, MigrationStage, RcMigrationStage}; -use polkadot_runtime::Runtime as Polkadot; +use polkadot_runtime::{Block as PolkadotBlock, Runtime as Polkadot}; +use polkadot_runtime_common::slots as pallet_slots; use std::str::FromStr; use super::mock::*; @@ -113,3 +115,37 @@ async fn account_migration_works() { // some overweight ones. }); } + +#[tokio::test] +async fn num_leases_to_ending_block_works_simple() { + let mut rc = remote_ext_test_setup::("SNAP_RC").await.unwrap(); + let f = |now: BlockNumberFor, num_leases: u32| { + frame_system::Pallet::::set_block_number(now); + pallet_rc_migrator::crowdloan::num_leases_to_ending_block::(num_leases) + }; + + rc.execute_with(|| { + let p = ::LeasePeriod::get(); + let o = ::LeaseOffset::get(); + + // Sanity check: + assert!(f(1000, 0).is_err()); + assert!(f(1000, 10).is_err()); + // Overflow check: + assert!(f(o, u32::MAX).is_err()); + + // In period 0: + assert_eq!(f(o, 0), Ok(o)); + assert_eq!(f(o, 1), Ok(o + p)); + assert_eq!(f(o, 2), Ok(o + 2 * p)); + + // In period 1: + assert_eq!(f(o + p, 0), Ok(o + p)); + assert_eq!(f(o + p, 1), Ok(o + 2 * p)); + assert_eq!(f(o + p, 2), Ok(o + 3 * p)); + + // In period 19 with 5 remaining: + assert_eq!(f(o + 19 * p, 1), Ok(o + 20 * p)); + assert_eq!(f(o + 19 * p, 5), Ok(o + 24 * p)); + }); +} diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index ce354602c1..d61555d4df 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -14,6 +14,10 @@ // along with Polkadot. If not, see . use crate::{types::AccountIdOf, *}; +use sp_runtime::{ + traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One}, + Saturating, +}; pub struct CrowdloanMigrator { _marker: sp_std::marker::PhantomData, @@ -156,6 +160,7 @@ impl PalletMigration for CrowdloanMigrator }; let Some((lease_acc, _)) = last_lease else { + // See https://github.com/paritytech/polkadot-sdk/blob/db3ff60b5af2a9017cb968a4727835f3d00340f0/polkadot/runtime/common/src/slots/mod.rs#L115 defensive!("Last lease cannot be None"); continue; }; @@ -168,7 +173,7 @@ impl PalletMigration for CrowdloanMigrator continue; } - let unreserve_block = num_leases_to_ending_block::(leases.len() as u32); + let unreserve_block = num_leases_to_ending_block::(leases.len() as u32).defensive().map_err(|_| Error::::Unreachable)?; log::debug!(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 }); @@ -207,7 +212,7 @@ impl PalletMigration for CrowdloanMigrator } let crowdloan_account = pallet_crowdloan::Pallet::::fund_account_id(fund.fund_index); - let withdraw_block = num_leases_to_ending_block::(leases.len() as u32); + let withdraw_block = num_leases_to_ending_block::(leases.len() as u32).defensive().map_err(|_| Error::::Unreachable)?; // We directly remove so that we dont have to store a cursor: pallet_crowdloan::Pallet::::contribution_kill(fund.fund_index, &contributor); @@ -231,7 +236,7 @@ impl PalletMigration for CrowdloanMigrator defensive_assert!(fund.raised == 0u32.into(), "Fund should be empty"); continue; } - let unreserve_block = num_leases_to_ending_block::(leases.len() as u32); + let unreserve_block = num_leases_to_ending_block::(leases.len() as u32).defensive().map_err(|_| Error::::Unreachable)?; log::debug!(target: LOG_TARGET, "Migrating out crowdloan deposit for para_id: {:?}, fund_index: {:?}, amount: {:?}, depositor: {:?}", ¶_id, &fund.fund_index, &fund.deposit, &fund.depositor); @@ -260,13 +265,31 @@ impl PalletMigration for CrowdloanMigrator } /// Calculate the lease ending block from the number of remaining leases (including the current). -// TODO tests -fn num_leases_to_ending_block(num_leases: u32) -> BlockNumberFor { +/// +/// # Example +/// +/// We are in the middle of period 3 and there are 2 leases left: +/// |-0-|-1-|-2-|-3-|-4-|-5-| +/// ^-----^ +/// Then this function returns the end block number of period 4 (start block of period 5). +pub fn num_leases_to_ending_block(num_leases: u32) -> Result, ()> { let now = frame_system::Pallet::::block_number(); let num_leases: BlockNumberFor = num_leases.into(); let offset = ::LeaseOffset::get(); let period = ::LeasePeriod::get(); - let current_period = (now - offset) / period; - (current_period + num_leases) * period + offset + // Sanity check: + if now < offset { + return Err(()); + } + + // The current period: (now - offset) / period + let current_period = now.checked_sub(&offset).and_then(|x| x.checked_div(&period)).ok_or(())?; + // (current_period + num_leases) * period + offset + let last_period_end_block = current_period + .checked_add(&num_leases) + .and_then(|x| x.checked_mul(&period)) + .and_then(|x| x.checked_add(&offset)) + .ok_or(())?; + Ok(last_period_end_block) } diff --git a/pallets/rc-migrator/src/lib.rs b/pallets/rc-migrator/src/lib.rs index e406454764..147b196480 100644 --- a/pallets/rc-migrator/src/lib.rs +++ b/pallets/rc-migrator/src/lib.rs @@ -362,8 +362,7 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The error that should to be replaced by something meaningful. - TODO, + Unreachable, OutOfWeight, /// Failed to send XCM message to AH. XcmError, From ce4f6629428aa9fd837bab0be069a4acb4c1c3fb Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 16:09:25 +0100 Subject: [PATCH 13/22] fix Signed-off-by: Oliver Tale-Yazdi --- pallets/rc-migrator/src/crowdloan.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index d61555d4df..95df115783 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -14,10 +14,7 @@ // along with Polkadot. If not, see . use crate::{types::AccountIdOf, *}; -use sp_runtime::{ - traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One}, - Saturating, -}; +use sp_runtime::traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; pub struct CrowdloanMigrator { _marker: sp_std::marker::PhantomData, @@ -159,12 +156,18 @@ impl PalletMigration for CrowdloanMigrator continue; }; - let Some((lease_acc, _)) = last_lease else { + let Some((lease_acc, lease_amount)) = last_lease else { // See https://github.com/paritytech/polkadot-sdk/blob/db3ff60b5af2a9017cb968a4727835f3d00340f0/polkadot/runtime/common/src/slots/mod.rs#L115 defensive!("Last lease cannot be None"); continue; }; + // Sanity check that all leases have the same account and amount: + for (acc, amount) in leases.iter().flatten() { + defensive_assert!(acc == lease_acc, "All leases should have the same account"); + defensive_assert!(amount == lease_amount, "All leases should have the same amount"); + } + // 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: crate::BalanceOf = leases.iter().flatten().map(|(_acc, amount)| amount).max().cloned().unwrap_or_default().into(); From b33b7b44231028679ef7def9d9bdcb3561696af6 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 16:12:00 +0100 Subject: [PATCH 14/22] fix Signed-off-by: Oliver Tale-Yazdi --- pallets/rc-migrator/src/crowdloan.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/rc-migrator/src/crowdloan.rs b/pallets/rc-migrator/src/crowdloan.rs index 95df115783..9be763a8a0 100644 --- a/pallets/rc-migrator/src/crowdloan.rs +++ b/pallets/rc-migrator/src/crowdloan.rs @@ -163,6 +163,7 @@ impl PalletMigration for CrowdloanMigrator }; // Sanity check that all leases have the same account and amount: + #[cfg(feature = "std")] for (acc, amount) in leases.iter().flatten() { defensive_assert!(acc == lease_acc, "All leases should have the same account"); defensive_assert!(amount == lease_amount, "All leases should have the same amount"); From 8fba1b1a19f10fb05425c0acd55ca35987cd4579 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 16:18:59 +0100 Subject: [PATCH 15/22] Remove deps Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 28 ------- pallets/ah-migrator/src/crowdloan.rs | 3 - pallets/ah-ops/Cargo.toml | 114 +++------------------------ 3 files changed, 10 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 223ac09871..db47da1c23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7692,42 +7692,14 @@ dependencies = [ name = "pallet-ah-ops" version = "0.1.0" dependencies = [ - "chrono", "cumulus-primitives-core", - "frame-benchmarking", "frame-support", "frame-system", - "hex", - "hex-literal", "log", - "pallet-asset-rate", - "pallet-bags-list", "pallet-balances", - "pallet-bounties", - "pallet-conviction-voting", - "pallet-fast-unstake", - "pallet-indices", - "pallet-multisig", - "pallet-nomination-pools", - "pallet-preimage", - "pallet-proxy", - "pallet-rc-migrator", - "pallet-referenda", - "pallet-scheduler", - "pallet-staking", - "pallet-state-trie-migration", "pallet-timestamp", - "pallet-treasury", - "pallet-vesting", "parity-scale-codec", - "polkadot-parachain-primitives", - "polkadot-runtime-common", - "polkadot-runtime-parachains", "scale-info", - "serde", - "sp-application-crypto 38.0.0", - "sp-core 34.0.0", - "sp-io 38.0.0", "sp-runtime 39.0.5", "sp-std", ] diff --git a/pallets/ah-migrator/src/crowdloan.rs b/pallets/ah-migrator/src/crowdloan.rs index 38a0f2fc99..7dfa76e5de 100644 --- a/pallets/ah-migrator/src/crowdloan.rs +++ b/pallets/ah-migrator/src/crowdloan.rs @@ -105,9 +105,6 @@ impl Pallet { } } -// extrinsic code -impl Pallet {} - pub struct CrowdloanMigrationCheck(pub PhantomData); #[cfg(feature = "std")] diff --git a/pallets/ah-ops/Cargo.toml b/pallets/ah-ops/Cargo.toml index 5b6d0cf204..c1b05296d1 100644 --- a/pallets/ah-ops/Cargo.toml +++ b/pallets/ah-ops/Cargo.toml @@ -10,136 +10,42 @@ repository.workspace = true [dependencies] codec = { workspace = true, features = ["max-encoded-len"] } cumulus-primitives-core = { workspace = true } -chrono = { workspace = true } -frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } log = { workspace = true } -pallet-asset-rate = { workspace = true } pallet-balances = { workspace = true } -pallet-bags-list = { workspace = true } -pallet-bounties = { workspace = true } -pallet-conviction-voting = { workspace = true } -pallet-fast-unstake = { workspace = true } -pallet-multisig = { workspace = true } -pallet-indices = { workspace = true } -pallet-nomination-pools = { workspace = true } -pallet-preimage = { workspace = true } -pallet-proxy = { workspace = true } -pallet-rc-migrator = { workspace = true } -pallet-referenda = { workspace = true } -pallet-scheduler = { workspace = true } -pallet-staking = { workspace = true } -pallet-state-trie-migration = { workspace = true } -pallet-vesting = { workspace = true } -pallet-treasury = { workspace = true } pallet-timestamp = { workspace = true } -polkadot-parachain-primitives = { workspace = true } -polkadot-runtime-common = { workspace = true } -runtime-parachains = { workspace = true } scale-info = { workspace = true, features = ["derive"] } -serde = { features = ["derive"], optional = true, workspace = true } -sp-application-crypto = { workspace = true } -sp-core = { workspace = true } -sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } -hex-literal = { workspace = true } -hex = { workspace = true } [features] default = ["std"] std = [ - "codec/std", - "frame-benchmarking?/std", + "cumulus-primitives-core/std", "frame-support/std", "frame-system/std", - "hex/std", "log/std", - "pallet-asset-rate/std", - "pallet-bags-list/std", "pallet-balances/std", - "pallet-bounties/std", - "pallet-conviction-voting/std", - "pallet-fast-unstake/std", - "pallet-indices/std", - "pallet-multisig/std", - "pallet-nomination-pools/std", - "pallet-preimage/std", - "pallet-proxy/std", - "pallet-rc-migrator/std", - "pallet-referenda/std", - "pallet-scheduler/std", - "pallet-staking/std", - "pallet-state-trie-migration/std", - "pallet-treasury/std", - "pallet-vesting/std", - "polkadot-parachain-primitives/std", - "polkadot-runtime-common/std", - "runtime-parachains/std", + "pallet-timestamp/std", + "codec/std", "scale-info/std", - "serde", - "sp-application-crypto/std", - "sp-core/std", - "sp-io/std", "sp-runtime/std", - "sp-std/std", - "chrono/std", - "cumulus-primitives-core/std", - "pallet-timestamp/std" + "sp-std/std" + ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-asset-rate/runtime-benchmarks", - "pallet-bags-list/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-bounties/runtime-benchmarks", - "pallet-conviction-voting/runtime-benchmarks", - "pallet-fast-unstake/runtime-benchmarks", - "pallet-indices/runtime-benchmarks", - "pallet-multisig/runtime-benchmarks", - "pallet-nomination-pools/runtime-benchmarks", - "pallet-preimage/runtime-benchmarks", - "pallet-proxy/runtime-benchmarks", - "pallet-rc-migrator/runtime-benchmarks", - "pallet-referenda/runtime-benchmarks", - "pallet-scheduler/runtime-benchmarks", - "pallet-staking/runtime-benchmarks", - "pallet-state-trie-migration/runtime-benchmarks", - "pallet-treasury/runtime-benchmarks", - "pallet-vesting/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", - "polkadot-runtime-common/runtime-benchmarks", - "runtime-parachains/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks" + "pallet-timestamp/runtime-benchmarks", + "sp-runtime/runtime-benchmarks" ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", - "pallet-asset-rate/try-runtime", - "pallet-bags-list/try-runtime", "pallet-balances/try-runtime", - "pallet-bounties/try-runtime", - "pallet-conviction-voting/try-runtime", - "pallet-fast-unstake/try-runtime", - "pallet-indices/try-runtime", - "pallet-multisig/try-runtime", - "pallet-nomination-pools/try-runtime", - "pallet-preimage/try-runtime", - "pallet-proxy/try-runtime", - "pallet-rc-migrator/try-runtime", - "pallet-referenda/try-runtime", - "pallet-scheduler/try-runtime", - "pallet-staking/try-runtime", - "pallet-state-trie-migration/try-runtime", - "pallet-treasury/try-runtime", - "pallet-vesting/try-runtime", - "polkadot-runtime-common/try-runtime", - "runtime-parachains/try-runtime", - "sp-runtime/try-runtime", - "pallet-timestamp/try-runtime" + "pallet-timestamp/try-runtime", + "sp-runtime/try-runtime" ] From 43705be6c0ea323703b13972ff23bb278f6b7b74 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 16:34:57 +0100 Subject: [PATCH 16/22] fmt Signed-off-by: Oliver Tale-Yazdi --- pallets/ah-migrator/Cargo.toml | 18 +++++++++--------- pallets/ah-ops/Cargo.toml | 10 +++++----- .../asset-hubs/asset-hub-polkadot/Cargo.toml | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pallets/ah-migrator/Cargo.toml b/pallets/ah-migrator/Cargo.toml index 6d20a9551e..bfdc0b1a9d 100644 --- a/pallets/ah-migrator/Cargo.toml +++ b/pallets/ah-migrator/Cargo.toml @@ -51,12 +51,15 @@ pallet-ah-ops = { workspace = true } [features] default = ["std"] std = [ + "chrono/std", "codec/std", + "cumulus-primitives-core/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "hex/std", "log/std", + "pallet-ah-ops/std", "pallet-asset-rate/std", "pallet-bags-list/std", "pallet-balances/std", @@ -73,6 +76,7 @@ std = [ "pallet-scheduler/std", "pallet-staking/std", "pallet-state-trie-migration/std", + "pallet-timestamp/std", "pallet-treasury/std", "pallet-vesting/std", "polkadot-parachain-primitives/std", @@ -85,15 +89,13 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", - "chrono/std", - "cumulus-primitives-core/std", - "pallet-ah-ops/std", - "pallet-timestamp/std" ] runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-ah-ops/runtime-benchmarks", "pallet-asset-rate/runtime-benchmarks", "pallet-bags-list/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -110,19 +112,18 @@ runtime-benchmarks = [ "pallet-scheduler/runtime-benchmarks", "pallet-staking/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "pallet-ah-ops/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks" ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-ah-ops/try-runtime", "pallet-asset-rate/try-runtime", "pallet-bags-list/try-runtime", "pallet-balances/try-runtime", @@ -139,11 +140,10 @@ try-runtime = [ "pallet-scheduler/try-runtime", "pallet-staking/try-runtime", "pallet-state-trie-migration/try-runtime", + "pallet-timestamp/try-runtime", "pallet-treasury/try-runtime", "pallet-vesting/try-runtime", "polkadot-runtime-common/try-runtime", "runtime-parachains/try-runtime", "sp-runtime/try-runtime", - "pallet-ah-ops/try-runtime", - "pallet-timestamp/try-runtime" ] diff --git a/pallets/ah-ops/Cargo.toml b/pallets/ah-ops/Cargo.toml index c1b05296d1..f950e5a2a7 100644 --- a/pallets/ah-ops/Cargo.toml +++ b/pallets/ah-ops/Cargo.toml @@ -22,17 +22,17 @@ sp-std = { workspace = true } [features] default = ["std"] std = [ + "codec/std", "cumulus-primitives-core/std", "frame-support/std", "frame-system/std", "log/std", "pallet-balances/std", "pallet-timestamp/std", - "codec/std", "scale-info/std", "sp-runtime/std", - "sp-std/std" - + "sp-std/std", + ] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", @@ -40,12 +40,12 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", - "sp-runtime/runtime-benchmarks" + "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", "pallet-timestamp/try-runtime", - "sp-runtime/try-runtime" + "sp-runtime/try-runtime", ] diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml index 926092a8b0..2456c2b9a2 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml @@ -149,6 +149,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "hex-literal", + "pallet-ah-ops/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-asset-rate/runtime-benchmarks", "pallet-assets/runtime-benchmarks", @@ -187,7 +188,6 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", - "pallet-ah-ops/runtime-benchmarks" ] try-runtime = [ "pallet-ah-migrator/try-runtime", @@ -201,6 +201,7 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "frame-try-runtime/try-runtime", + "pallet-ah-ops/try-runtime", "pallet-asset-conversion-tx-payment/try-runtime", "pallet-asset-conversion/try-runtime", "pallet-asset-rate/try-runtime", @@ -237,7 +238,6 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", "system-parachains-common/try-runtime", - "pallet-ah-ops/try-runtime" ] std = [ "pallet-ah-migrator/std", @@ -267,6 +267,7 @@ std = [ "frame-try-runtime?/std", "kusama-runtime-constants/std", "log/std", + "pallet-ah-ops/std", "pallet-asset-conversion-tx-payment/std", "pallet-asset-conversion/std", "pallet-asset-rate/std", @@ -337,7 +338,6 @@ std = [ "xcm-executor/std", "xcm-runtime-apis/std", "xcm/std", - "pallet-ah-ops/std" ] # Enable metadata hash generation at compile time for the `CheckMetadataHash` extension. From ff8e665d7827f262f7f0f8b2bba5d598801d67a2 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 17:07:49 +0100 Subject: [PATCH 17/22] Update pallets/ah-migrator/src/lib.rs Co-authored-by: muharem --- pallets/ah-migrator/src/lib.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index 6d20fc3f08..9cee52fbf9 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -211,15 +211,6 @@ pub mod pallet { /// Failed to integrate a vesting schedule. FailedToIntegrateVestingSchedule, Unreachable, - /// Either no lease deposit or already unreserved. - NoLeaseReserve, - /// Either no crowdloan contribution or already withdrawn. - NoCrowdloanContribution, - NoCrowdloanReserve, - /// Failed to withdraw crowdloan contribution. - FailedToWithdrawCrowdloanContribution, - /// Block number is not yet reached. - NotYet, } #[pallet::event] From c884f68a1b413d2b21588e960d4f6a832a486597 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 17:08:05 +0100 Subject: [PATCH 18/22] Update pallets/ah-migrator/src/lib.rs Co-authored-by: muharem --- pallets/ah-migrator/src/lib.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index 9cee52fbf9..361c31c307 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -219,20 +219,6 @@ pub mod pallet { /// The event that should to be replaced by something meaningful. TODO, - /// Some lease reserve could not be unreserved and needs manual cleanup. - LeaseUnreserveRemaining { - depositor: T::AccountId, - para_id: ParaId, - remaining: BalanceOf, - }, - - /// Some amount for a crowdloan reserve could not be unreserved and needs manual cleanup. - CrowdloanUnreserveRemaining { - depositor: T::AccountId, - para_id: ParaId, - remaining: BalanceOf, - }, - /// We received a batch of accounts that we are going to integrate. AccountBatchReceived { /// How many accounts are in the batch. From 68abe6a330cfc92079f9d0fc5523ad0397170aea Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 17:08:12 +0100 Subject: [PATCH 19/22] Update pallets/ah-ops/src/lib.rs Co-authored-by: muharem --- pallets/ah-ops/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/ah-ops/src/lib.rs b/pallets/ah-ops/src/lib.rs index 0dd9e54d39..bd45c71c5a 100644 --- a/pallets/ah-ops/src/lib.rs +++ b/pallets/ah-ops/src/lib.rs @@ -151,7 +151,6 @@ pub mod pallet { #[pallet::error] pub enum Error { - TODO, /// Either no lease deposit or already unreserved. NoLeaseReserve, /// Either no crowdloan contribution or already withdrawn. From e04a36102aa3948e5bb822a1f525da1069c644c0 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 20 Feb 2025 17:14:33 +0100 Subject: [PATCH 20/22] no dev mode Signed-off-by: Oliver Tale-Yazdi --- pallets/ah-migrator/src/lib.rs | 1 - pallets/ah-ops/src/lib.rs | 5 ++++- pallets/rc-migrator/src/accounts.rs | 2 +- pallets/rc-migrator/src/lib.rs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs index 361c31c307..e9542ada32 100644 --- a/pallets/ah-migrator/src/lib.rs +++ b/pallets/ah-migrator/src/lib.rs @@ -51,7 +51,6 @@ pub mod vesting; pub use pallet::*; pub use pallet_rc_migrator::types::ZeroWeightOr; -use cumulus_primitives_core::ParaId; use frame_support::{ pallet_prelude::*, storage::{transactional::with_transaction_opaque_err, TransactionOutcome}, diff --git a/pallets/ah-ops/src/lib.rs b/pallets/ah-ops/src/lib.rs index bd45c71c5a..7eba5947e4 100644 --- a/pallets/ah-ops/src/lib.rs +++ b/pallets/ah-ops/src/lib.rs @@ -52,7 +52,7 @@ pub const LOG_TARGET: &str = "runtime::ah-migrator"; pub type BalanceOf = ::Balance; -#[frame_support::pallet(dev_mode)] +#[frame_support::pallet] pub mod pallet { use super::*; @@ -197,6 +197,7 @@ pub mod pallet { /// /// Solo bidder accounts that won lease auctions can use this to unreserve their amount. #[pallet::call_index(0)] + #[pallet::weight(0)] pub fn unreserve_lease_deposit( origin: OriginFor, block: BlockNumberFor, @@ -217,6 +218,7 @@ pub mod pallet { /// /// Can be called by any signed origin. #[pallet::call_index(1)] + #[pallet::weight(0)] pub fn withdraw_crowdloan_contribution( origin: OriginFor, block: BlockNumberFor, @@ -238,6 +240,7 @@ pub mod pallet { /// Can be called by any signed origin. The condition that all contributions are withdrawn /// is in place since the reserve acts as a storage deposit. #[pallet::call_index(2)] + #[pallet::weight(0)] pub fn unreserve_crowdloan_reserve( origin: OriginFor, block: BlockNumberFor, diff --git a/pallets/rc-migrator/src/accounts.rs b/pallets/rc-migrator/src/accounts.rs index e00c956879..1d08e279ff 100644 --- a/pallets/rc-migrator/src/accounts.rs +++ b/pallets/rc-migrator/src/accounts.rs @@ -120,7 +120,7 @@ pub struct Account { } /// The state for the Relay Chain accounts. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum AccountState { /// The account should be migrated to AH and removed on RC. Migrate, diff --git a/pallets/rc-migrator/src/lib.rs b/pallets/rc-migrator/src/lib.rs index 147b196480..910f36619d 100644 --- a/pallets/rc-migrator/src/lib.rs +++ b/pallets/rc-migrator/src/lib.rs @@ -294,7 +294,7 @@ impl = AccountInfo<::Nonce, ::AccountData>; -#[frame_support::pallet(dev_mode)] +#[frame_support::pallet] pub mod pallet { use super::*; From a473017c57b4995306e02a5a5756d103848decd8 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 21 Feb 2025 13:03:50 +0100 Subject: [PATCH 21/22] fix Signed-off-by: Oliver Tale-Yazdi --- integration-tests/ahm/Cargo.toml | 2 -- integration-tests/ahm/src/tests.rs | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/integration-tests/ahm/Cargo.toml b/integration-tests/ahm/Cargo.toml index ef19980083..9ee9117d11 100644 --- a/integration-tests/ahm/Cargo.toml +++ b/integration-tests/ahm/Cargo.toml @@ -41,5 +41,3 @@ sp-tracing = { workspace = true, default-features = true } tokio = { features = ["full", "macros"], workspace = true } xcm-builder = { workspace = true, default-features = true } xcm-emulator = { workspace = true, default-features = true } -polkadot-runtime-common = { workspace = true, default-features = true } -codec = { workspace = true, default-features = true } diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs index cb1682b877..3f5b5e830a 100644 --- a/integration-tests/ahm/src/tests.rs +++ b/integration-tests/ahm/src/tests.rs @@ -37,10 +37,7 @@ use frame_support::traits::*; use frame_system::pallet_prelude::BlockNumberFor; use pallet_rc_migrator::{types::PalletMigrationChecks, MigrationStage, RcMigrationStage}; use polkadot_runtime::{Block as PolkadotBlock, Runtime as Polkadot}; -use polkadot_runtime_common::slots as pallet_slots; -use std::str::FromStr; -use polkadot_runtime::Runtime as Polkadot; -use polkadot_runtime_common::paras_registrar; +use polkadot_runtime_common::{paras_registrar, slots as pallet_slots}; use sp_runtime::AccountId32; use std::{collections::BTreeMap, str::FromStr}; use xcm_emulator::ConvertLocation; From 86038afe1808cfb723e9799a830f28defd241337 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 21 Feb 2025 13:09:02 +0100 Subject: [PATCH 22/22] zepter Signed-off-by: Oliver Tale-Yazdi --- integration-tests/ahm/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/ahm/Cargo.toml b/integration-tests/ahm/Cargo.toml index 9ee9117d11..eee71d5df7 100644 --- a/integration-tests/ahm/Cargo.toml +++ b/integration-tests/ahm/Cargo.toml @@ -40,4 +40,4 @@ sp-storage = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } tokio = { features = ["full", "macros"], workspace = true } xcm-builder = { workspace = true, default-features = true } -xcm-emulator = { workspace = true, default-features = true } +xcm-emulator = { workspace = true }