diff --git a/Cargo.lock b/Cargo.lock
index fbad58d4f8..cab5c2e1aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7630,8 +7630,10 @@ dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
+ "hex-literal",
"log",
"pallet-balances",
+ "pallet-multisig",
"pallet-nomination-pools",
"pallet-preimage",
"pallet-rc-migrator",
@@ -8856,6 +8858,7 @@ dependencies = [
"frame-system",
"log",
"pallet-balances",
+ "pallet-multisig",
"pallet-staking",
"parity-scale-codec",
"polkadot-parachain-primitives",
diff --git a/integration-tests/ahm/src/mock.rs b/integration-tests/ahm/src/mock.rs
index 529cef3f10..e551aec215 100644
--- a/integration-tests/ahm/src/mock.rs
+++ b/integration-tests/ahm/src/mock.rs
@@ -15,8 +15,11 @@
// along with Polkadot. If not, see .
use asset_hub_polkadot_runtime::Block as AssetHubBlock;
+use cumulus_primitives_core::{AggregateMessageOrigin, InboundDownwardMessage};
+use frame_support::traits::EnqueueMessage;
use polkadot_runtime::Block as PolkadotBlock;
use remote_externalities::{Builder, Mode, OfflineConfig, RemoteExternalities};
+use sp_runtime::BoundedVec;
const LOG_RC: &str = "runtime::relay";
const LOG_AH: &str = "runtime::asset-hub";
@@ -73,3 +76,17 @@ pub fn next_block_ah() {
frame_system::Pallet::::reset_events();
>::on_initialize(now + 1);
}
+
+/// Enqueue DMP messages on the parachain side.
+///
+/// This bypasses `set_validation_data` and `enqueue_inbound_downward_messages` by just directly
+/// enqueuing them.
+pub fn enqueue_dmp(msgs: Vec) {
+ for msg in msgs {
+ let bounded_msg: BoundedVec = msg.msg.try_into().expect("DMP message too big");
+ asset_hub_polkadot_runtime::MessageQueue::enqueue_message(
+ bounded_msg.as_bounded_slice(),
+ AggregateMessageOrigin::Parent,
+ );
+ }
+}
diff --git a/integration-tests/ahm/src/tests.rs b/integration-tests/ahm/src/tests.rs
index fdeec6a55a..511fe5e433 100644
--- a/integration-tests/ahm/src/tests.rs
+++ b/integration-tests/ahm/src/tests.rs
@@ -33,13 +33,10 @@
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
use frame_support::{pallet_prelude::*, traits::*, weights::WeightMeter};
-use pallet_rc_migrator::RcMigrationStage;
+use pallet_rc_migrator::{MigrationStage, RcMigrationStage};
use polkadot_primitives::InboundDownwardMessage;
-use polkadot_runtime::RcMigrator;
-use sp_core::H256;
-use sp_io::TestExternalities;
-use sp_storage::StateVersion;
-use std::cell::OnceCell;
+use remote_externalities::RemoteExternalities;
+use tokio::sync::mpsc::channel;
use asset_hub_polkadot_runtime::{Block as AssetHubBlock, Runtime as AssetHub};
use polkadot_runtime::{Block as PolkadotBlock, Runtime as Polkadot};
@@ -61,13 +58,15 @@ async fn account_migration_works() {
let new_dmps =
runtime_parachains::dmp::DownwardMessageQueues::::take(para_id);
- if new_dmps.is_empty() && !dmps.is_empty() {
- break;
- }
dmps.extend(new_dmps);
- }
- dmps
+ if RcMigrationStage::::get() ==
+ pallet_rc_migrator::MigrationStage::MultisigMigrationDone
+ {
+ log::info!("Multisig migration done");
+ break dmps;
+ }
+ }
});
rc.commit_all().unwrap();
log::info!("Num of RC->AH DMP messages: {}", dmp_messages.len());
@@ -95,17 +94,3 @@ async fn account_migration_works() {
// some overweight ones.
});
}
-
-/// Enqueue DMP messages on the parachain side.
-///
-/// This bypasses `set_validation_data` and `enqueue_inbound_downward_messages` by just directly
-/// enqueuing them.
-fn enqueue_dmp(msgs: Vec) {
- for msg in msgs {
- let bounded_msg: BoundedVec = msg.msg.try_into().expect("DMP message too big");
- asset_hub_polkadot_runtime::MessageQueue::enqueue_message(
- bounded_msg.as_bounded_slice(),
- AggregateMessageOrigin::Parent,
- );
- }
-}
diff --git a/pallets/ah-migrator/Cargo.toml b/pallets/ah-migrator/Cargo.toml
index 491d55cb78..4cfe6176dd 100644
--- a/pallets/ah-migrator/Cargo.toml
+++ b/pallets/ah-migrator/Cargo.toml
@@ -14,6 +14,7 @@ frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
pallet-balances = { workspace = true }
+pallet-multisig = { workspace = true }
pallet-nomination-pools = { workspace = true }
pallet-preimage = { workspace = true }
pallet-rc-migrator = { workspace = true }
@@ -29,6 +30,7 @@ sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
+hex-literal = { workspace = true }
[features]
default = ["std"]
@@ -39,6 +41,7 @@ std = [
"frame-system/std",
"log/std",
"pallet-balances/std",
+ "pallet-multisig/std",
"pallet-nomination-pools/std",
"pallet-preimage/std",
"pallet-rc-migrator/std",
@@ -60,6 +63,7 @@ runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
+ "pallet-multisig/runtime-benchmarks",
"pallet-nomination-pools/runtime-benchmarks",
"pallet-preimage/runtime-benchmarks",
"pallet-rc-migrator/runtime-benchmarks",
@@ -74,6 +78,7 @@ try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-balances/try-runtime",
+ "pallet-multisig/try-runtime",
"pallet-nomination-pools/try-runtime",
"pallet-preimage/try-runtime",
"pallet-rc-migrator/try-runtime",
diff --git a/pallets/ah-migrator/src/account.rs b/pallets/ah-migrator/src/account.rs
new file mode 100644
index 0000000000..ede5de8baf
--- /dev/null
+++ b/pallets/ah-migrator/src/account.rs
@@ -0,0 +1,113 @@
+// 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.
+
+//! Account balance migration.
+
+use crate::{types::*, *};
+
+impl Pallet {
+ pub fn do_receive_accounts(
+ accounts: Vec>,
+ ) -> Result<(), Error> {
+ log::info!(target: LOG_TARGET, "Integrating {} accounts", accounts.len());
+
+ for account in accounts {
+ let _: Result<(), ()> = with_transaction_opaque_err::<(), (), _>(|| {
+ match Self::do_receive_account(account) {
+ Ok(()) => TransactionOutcome::Commit(Ok(())),
+ Err(_) => TransactionOutcome::Rollback(Ok(())),
+ }
+ })
+ .expect("Always returning Ok; qed");
+ }
+
+ Ok(())
+ }
+
+ /// MAY CHANGED STORAGE ON ERROR RETURN
+ pub fn do_receive_account(
+ account: RcAccount,
+ ) -> Result<(), Error> {
+ let who = account.who;
+ let total_balance = account.free + account.reserved;
+ let minted = match ::Currency::mint_into(&who, total_balance) {
+ Ok(minted) => minted,
+ Err(e) => {
+ log::error!(target: LOG_TARGET, "Failed to mint into account {}: {:?}", who.to_ss58check(), e);
+ return Err(Error::::TODO);
+ },
+ };
+ debug_assert!(minted == total_balance);
+
+ for hold in account.holds {
+ if let Err(e) = ::Currency::hold(
+ &T::RcToAhHoldReason::convert(hold.id),
+ &who,
+ hold.amount,
+ ) {
+ log::error!(target: LOG_TARGET, "Failed to hold into account {}: {:?}", who.to_ss58check(), e);
+ return Err(Error::::TODO);
+ }
+ }
+
+ if let Err(e) = ::Currency::reserve(&who, account.unnamed_reserve) {
+ log::error!(target: LOG_TARGET, "Failed to reserve into account {}: {:?}", who.to_ss58check(), e);
+ return Err(Error::::TODO);
+ }
+
+ for freeze in account.freezes {
+ if let Err(e) = ::Currency::set_freeze(
+ &T::RcToAhFreezeReason::convert(freeze.id),
+ &who,
+ freeze.amount,
+ ) {
+ log::error!(target: LOG_TARGET, "Failed to freeze into account {}: {:?}", who.to_ss58check(), e);
+ return Err(Error::::TODO);
+ }
+ }
+
+ for lock in account.locks {
+ ::Currency::set_lock(
+ lock.id,
+ &who,
+ lock.amount,
+ types::map_lock_reason(lock.reasons),
+ );
+ }
+
+ log::debug!(
+ target: LOG_TARGET,
+ "Integrating account: {}", who.to_ss58check(),
+ );
+
+ // TODO run some post-migration sanity checks
+
+ // Apply all additional consumers that were excluded from the balance stuff above:
+ for _ in 0..account.consumers {
+ if let Err(e) = frame_system::Pallet::::inc_consumers(&who) {
+ log::error!(target: LOG_TARGET, "Failed to inc consumers for account {}: {:?}", who.to_ss58check(), e);
+ return Err(Error::::TODO);
+ }
+ }
+ for _ in 0..account.providers {
+ frame_system::Pallet::::inc_providers(&who);
+ }
+
+ // TODO: publish event
+ Ok(())
+ }
+}
diff --git a/pallets/ah-migrator/src/lib.rs b/pallets/ah-migrator/src/lib.rs
index 467b115b6a..57bda347f1 100644
--- a/pallets/ah-migrator/src/lib.rs
+++ b/pallets/ah-migrator/src/lib.rs
@@ -31,6 +31,8 @@
#![cfg_attr(not(feature = "std"), no_std)]
+pub mod account;
+pub mod multisig;
pub mod types;
pub use pallet::*;
@@ -44,7 +46,7 @@ use frame_support::{
};
use frame_system::pallet_prelude::*;
use pallet_balances::{AccountData, Reasons as LockReasons};
-use pallet_rc_migrator::accounts::Account as RcAccount;
+use pallet_rc_migrator::{accounts::Account as RcAccount, multisig::*};
use sp_application_crypto::Ss58Codec;
use sp_runtime::{traits::Convert, AccountId32};
use sp_std::prelude::*;
@@ -62,6 +64,7 @@ pub mod pallet {
pub trait Config:
frame_system::Config, AccountId = AccountId32>
+ pallet_balances::Config
+ + pallet_multisig::Config
{
/// The overarching event type.
type RuntimeEvent: From> + IsType<::RuntimeEvent>;
@@ -91,10 +94,21 @@ pub mod pallet {
}
#[pallet::event]
- //#[pallet::generate_deposit(pub(crate) fn deposit_event)]
+ #[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event {
/// TODO
TODO,
+ /// We received a batch of multisigs that we are going to integrate.
+ MultisigBatchReceived {
+ /// How many multisigs are in the batch.
+ count: u32,
+ },
+ MultisigBatchProcessed {
+ /// How many multisigs were successfully integrated.
+ count_good: u32,
+ /// How many multisigs failed to integrate.
+ count_bad: u32,
+ },
}
#[pallet::pallet]
@@ -109,7 +123,6 @@ pub mod pallet {
///
/// The accounts that sent with `pallet_rc_migrator::Pallet::migrate_accounts` function.
#[pallet::call_index(0)]
- #[pallet::weight({1})]
pub fn receive_accounts(
origin: OriginFor,
accounts: Vec>,
@@ -121,96 +134,20 @@ pub mod pallet {
Ok(())
}
- }
-
- impl Pallet {
- fn do_receive_accounts(
- accounts: Vec>,
- ) -> Result<(), Error> {
- log::debug!(target: LOG_TARGET, "Integrating {} accounts", accounts.len());
-
- for account in accounts {
- let _: Result<(), ()> = with_transaction_opaque_err::<(), (), _>(|| {
- match Self::do_receive_account(account) {
- Ok(()) => TransactionOutcome::Commit(Ok(())),
- Err(_) => TransactionOutcome::Rollback(Ok(())),
- }
- })
- .expect("Always returning Ok; qed");
- }
-
- Ok(())
- }
- /// MAY CHANGED STORAGE ON ERROR RETURN
- fn do_receive_account(
- account: RcAccount,
- ) -> Result<(), Error> {
- let who = account.who;
- let total_balance = account.free + account.reserved;
- let minted = match T::Currency::mint_into(&who, total_balance) {
- Ok(minted) => minted,
- Err(e) => {
- log::error!(target: LOG_TARGET, "Failed to mint into account {}: {:?}", who.to_ss58check(), e);
- return Err(Error::::TODO);
- },
- };
- debug_assert!(minted == total_balance);
-
- for hold in account.holds {
- if let Err(e) =
- T::Currency::hold(&T::RcToAhHoldReason::convert(hold.id), &who, hold.amount)
- {
- log::error!(target: LOG_TARGET, "Failed to hold into account {}: {:?}", who.to_ss58check(), e);
- return Err(Error::::TODO);
- }
- }
-
- if let Err(e) = T::Currency::reserve(&who, account.unnamed_reserve) {
- log::error!(target: LOG_TARGET, "Failed to reserve into account {}: {:?}", who.to_ss58check(), e);
- return Err(Error::::TODO);
- }
-
- for freeze in account.freezes {
- if let Err(e) = T::Currency::set_freeze(
- &T::RcToAhFreezeReason::convert(freeze.id),
- &who,
- freeze.amount,
- ) {
- log::error!(target: LOG_TARGET, "Failed to freeze into account {}: {:?}", who.to_ss58check(), e);
- return Err(Error::::TODO);
- }
- }
-
- for lock in account.locks {
- T::Currency::set_lock(
- lock.id,
- &who,
- lock.amount,
- types::map_lock_reason(lock.reasons),
- );
- }
-
- log::debug!(
- target: LOG_TARGET,
- "Integrating account: {}", who.to_ss58check(),
- );
-
- // TODO run some post-migration sanity checks
-
- // Apply all additional consumers that were excluded from the balance stuff above:
- for _ in 0..account.consumers {
- if let Err(e) = frame_system::Pallet::::inc_consumers(&who) {
- log::error!(target: LOG_TARGET, "Failed to inc consumers for account {}: {:?}", who.to_ss58check(), e);
- return Err(Error::::TODO);
- }
- }
- for _ in 0..account.providers {
- frame_system::Pallet::::inc_providers(&who);
- }
+ /// Receive multisigs from the Relay Chain.
+ ///
+ /// This will be called from an XCM `Transact` inside a DMP from the relay chain. The
+ /// multisigs were prepared by
+ /// `pallet_rc_migrator::multisig::MultisigMigrator::migrate_many`.
+ #[pallet::call_index(1)]
+ pub fn receive_multisigs(
+ origin: OriginFor,
+ accounts: Vec>,
+ ) -> DispatchResult {
+ ensure_root(origin)?;
- // TODO: publish event
- Ok(())
+ Self::do_receive_multisigs(accounts).map_err(Into::into)
}
}
diff --git a/pallets/ah-migrator/src/multisig.rs b/pallets/ah-migrator/src/multisig.rs
new file mode 100644
index 0000000000..ebac37cf03
--- /dev/null
+++ b/pallets/ah-migrator/src/multisig.rs
@@ -0,0 +1,55 @@
+use crate::{types::*, *};
+use hex_literal::hex;
+
+/// These multisigs have historical issues where the deposit is missing for the creator.
+const KNOWN_BAD_MULTISIGS: &[AccountId32] = &[
+ AccountId32::new(hex!("e64d5c0de81b9c960c1dd900ad2a5d9d91c8a683e60dd1308e6bc7f80ea3b25f")),
+ AccountId32::new(hex!("d55ec415b6703ddf7bec9d5c02a0b642f1f5bd068c6b3c50c2145544046f1491")),
+ AccountId32::new(hex!("c2ff4f84b7fcee1fb04b4a97800e72321a4bc9939d456ad48d971127fc661c48")),
+ AccountId32::new(hex!("0a8933d3f2164648399cc48cb8bb8c915abb94a2164c40ad6b48cee005f1cb6e")),
+ AccountId32::new(hex!("ebe3cd53e580c4cd88acec1c952585b50a44a9b697d375ff648fee582ae39d38")),
+ AccountId32::new(hex!("e64d5c0de81b9c960c1dd900ad2a5d9d91c8a683e60dd1308e6bc7f80ea3b25f")),
+ AccountId32::new(hex!("caafae0aaa6333fcf4dc193146945fe8e4da74aa6c16d481eef0ca35b8279d73")),
+ AccountId32::new(hex!("d429458e57ba6e9b21688441ff292c7cf82700550446b061a6c5dec306e1ef05")),
+];
+
+impl Pallet {
+ pub fn do_receive_multisigs(multisigs: Vec>) -> Result<(), Error> {
+ Self::deposit_event(Event::MultisigBatchReceived { count: multisigs.len() as u32 });
+ let (mut count_good, mut count_bad) = (0, 0);
+ log::info!(target: LOG_TARGET, "Integrating {} multisigs", multisigs.len());
+
+ for multisig in multisigs {
+ match Self::do_receive_multisig(multisig) {
+ Ok(()) => count_good += 1,
+ Err(e) => {
+ count_bad += 1;
+ log::error!(target: LOG_TARGET, "Error while integrating multisig: {:?}", e);
+ },
+ }
+ }
+ Self::deposit_event(Event::MultisigBatchProcessed { count_good, count_bad });
+
+ Ok(())
+ }
+
+ pub fn do_receive_multisig(multisig: RcMultisigOf) -> Result<(), Error> {
+ log::debug!(target: LOG_TARGET, "Integrating multisig {}, {:?}", multisig.creator.to_ss58check(), multisig.deposit);
+
+ let missing = ::Currency::unreserve(
+ &multisig.creator,
+ multisig.deposit,
+ );
+ if missing != Default::default() {
+ if KNOWN_BAD_MULTISIGS.contains(&multisig.creator) {
+ log::warn!(target: LOG_TARGET, "Failed to unreserve deposit for known bad multisig {}, missing: {:?}", multisig.creator.to_ss58check(), missing);
+
+ log::warn!("{:?}", frame_system::Account::::get(&multisig.creator));
+ } else {
+ log::error!(target: LOG_TARGET, "Failed to unreserve deposit for multisig {} missing {:?}, details: {:?}", multisig.creator.to_ss58check(), missing, multisig.details);
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/pallets/rc-migrator/Cargo.toml b/pallets/rc-migrator/Cargo.toml
index 7a83f4fb1c..d5419850f5 100644
--- a/pallets/rc-migrator/Cargo.toml
+++ b/pallets/rc-migrator/Cargo.toml
@@ -12,7 +12,6 @@ codec = { workspace = true, features = ["max-encoded-len"] }
scale-info = { workspace = true, features = ["derive"] }
serde = { features = ["derive"], optional = true, workspace = true }
log = { workspace = true }
-
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
@@ -20,10 +19,9 @@ sp-core = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
sp-io = { workspace = true }
-
pallet-balances = { workspace = true }
pallet-staking = { workspace = true }
-
+pallet-multisig = { workspace = true }
polkadot-runtime-common = { workspace = true }
runtime-parachains = { workspace = true }
polkadot-parachain-primitives = { workspace = true }
@@ -39,6 +37,7 @@ std = [
"frame-system/std",
"log/std",
"pallet-balances/std",
+ "pallet-multisig/std",
"pallet-staking/std",
"polkadot-parachain-primitives/std",
"polkadot-runtime-common/std",
@@ -56,6 +55,7 @@ runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
+ "pallet-multisig/runtime-benchmarks",
"pallet-staking/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
"polkadot-runtime-common/runtime-benchmarks",
@@ -66,6 +66,7 @@ try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-balances/try-runtime",
+ "pallet-multisig/try-runtime",
"pallet-staking/try-runtime",
"polkadot-runtime-common/try-runtime",
"runtime-parachains/try-runtime",
diff --git a/pallets/rc-migrator/src/accounts.rs b/pallets/rc-migrator/src/accounts.rs
index 0046196ab4..31a7e97d44 100644
--- a/pallets/rc-migrator/src/accounts.rs
+++ b/pallets/rc-migrator/src/accounts.rs
@@ -74,6 +74,7 @@ use crate::{types::*, *};
use frame_support::{traits::tokens::IdAmount, weights::WeightMeter};
use frame_system::Account as SystemAccount;
use pallet_balances::{AccountData, BalanceLock};
+use sp_runtime::traits::Zero;
/// Account type meant to transfer data between RC and AH.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
@@ -159,23 +160,22 @@ impl Pallet {
/// Get the first account that the migration should begin with.
///
/// Returns `None` when there are no accounts present.
- pub fn first_account(_weight: &mut WeightMeter) -> Result