From 8e17accd7cd61ca73a45b14e6053455502d9927c Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 31 May 2023 15:51:46 +0200 Subject: [PATCH 01/13] Add new Barrier and OriginConverter to support used-paid XCM fees --- Cargo.lock | 3 + crates/kilt-dip-support/Cargo.toml | 12 ++- crates/kilt-dip-support/src/lib.rs | 1 + crates/kilt-dip-support/src/xcm.rs | 143 +++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 crates/kilt-dip-support/src/xcm.rs diff --git a/Cargo.lock b/Cargo.lock index d033efc860..4c064cc994 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4220,6 +4220,7 @@ dependencies = [ name = "kilt-dip-support" version = "1.11.0-dev" dependencies = [ + "cumulus-primitives-core", "did", "frame-support", "frame-system", @@ -4231,6 +4232,8 @@ dependencies = [ "sp-runtime", "sp-std", "sp-trie", + "xcm", + "xcm-executor", ] [[package]] diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index e06e17664e..aed093c72d 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -28,6 +28,13 @@ sp-core.workspace = true sp-trie.workspace = true sp-std.workspace = true +# Polkadot dependencies +xcm.workspace = true +xcm-executor.workspace = true + +# Cumulus dependencies +cumulus-primitives-core.workspace = true + [features] default = ["std"] std = [ @@ -41,6 +48,9 @@ std = [ "sp-runtime/std", "sp-core/std", "sp-trie/std", - "sp-std/std" + "sp-std/std", + "xcm-executor/std", + "xcm/std", + "cumulus-primitives-core/std" ] runtime-benchmarks = [] diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 103a5c504f..f4c1a05e4a 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -28,6 +28,7 @@ use crate::did::MerkleLeavesAndDidSignature; pub mod did; pub mod merkle; pub mod traits; +pub mod xcm; /// A type that chains a Merkle proof verification with a DID signature /// verification. The required input of this type is a tuple (A, B) where A is diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs new file mode 100644 index 0000000000..ef8146aae4 --- /dev/null +++ b/crates/kilt-dip-support/src/xcm.rs @@ -0,0 +1,143 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain 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. + +// The KILT Blockchain 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 this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use cumulus_primitives_core::{Junction::AccountKey20, OriginKind, ParaId}; +use frame_support::ensure; +use sp_core::Get; +use sp_std::marker::PhantomData; +use xcm::latest::{ + Instruction, + Instruction::{BuyExecution, DescendOrigin, Transact, WithdrawAsset}, + Junction::{AccountId32, Parachain}, + Junctions::{X1, X2}, + MultiLocation, Weight, +}; +use xcm_executor::traits::{ConvertOrigin, ShouldExecute}; + +// Allows a parachain to descend to an `X1(AccountId32)` or `X1(AccountId20)` +// junction, withdraw fees from their balance, and then carry on with a +// `Transact`. +pub struct AllowParachainProviderAsSubaccount(PhantomData); + +impl ShouldExecute for AllowParachainProviderAsSubaccount +where + ProviderParaId: Get, +{ + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + // Ensure that the origin is a parachain allowed to act as identity provider. + ensure!(*origin == Parachain(ProviderParaId::get().into()).into(), ()); + let mut iter = instructions.iter(); + // This must match the implementation of the `IdentityProofDispatcher` trait. + // TODO: Refactor so that they depend on each other and we avoid duplication + match (iter.next(), iter.next(), iter.next(), iter.next(), iter.next()) { + ( + Some(DescendOrigin(X1(AccountId32 { .. } | AccountKey20 { .. }))), + Some(WithdrawAsset { .. }), + Some(BuyExecution { .. }), + Some(Transact { + origin_kind: OriginKind::Native, + .. + }), + None, + ) => Ok(()), + _ => Err(()), + } + } +} + +// Decorate an existing Barrier to add one more check to allow a sibling +// parachain as the DIP provider. +pub struct OrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); + +impl ShouldExecute for OrElseCheckForParachainProvider +where + Barrier: ShouldExecute, + ProviderParaId: Get, +{ + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + } +} + +pub struct AccountIdJunctionToParachainOriginConverter( + PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, +); + +impl ConvertOrigin + for AccountIdJunctionToParachainOriginConverter +where + ProviderParaId: Get, + ParachainOrigin: From, + RuntimeOrigin: From, +{ + fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { + let origin = origin.into(); + let provider_para_id: u32 = ProviderParaId::get().into(); + match (kind, origin) { + ( + OriginKind::Native, + MultiLocation { + parents: 1, + interior: X2(Parachain(para_id), AccountId32 { .. } | AccountKey20 { .. }), + }, + ) if para_id == provider_para_id => Ok(ParachainOrigin::from(ProviderParaId::get()).into()), + _ => Err(origin), + } + } +} + +// // Decorate an existing OriginConverter to add the conversion of a sibling +// // parachain as the DIP provider. +// pub struct OrElseSiblingParachainProviderConverter( +// PhantomData<(OriginConverter, ProviderParaId, ParachainOrigin, +// RuntimeOrigin)>, ); + +// impl +// ConvertOrigin +// for OrElseSiblingParachainProviderConverter where +// OriginConverter: ConvertOrigin, +// ProviderParaId: Get, +// ParachainOrigin: From, +// RuntimeOrigin: From, +// { +// fn convert_origin(origin: impl Into, kind: OriginKind) -> +// Result { +// OriginConverter::convert_origin(origin, kind)?; +// AccountIdJunctionToParachainOriginConverter::::convert_origin( origin, kind, +// ) +// } +// } From ef1e6576f810f88c38121bd6ccc84b7bf7db55ec Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 1 Jun 2023 15:13:30 +0200 Subject: [PATCH 02/13] Tests passing --- Cargo.lock | 1 + crates/kilt-dip-support/src/xcm.rs | 61 ++++++++++++------- .../runtimes/dip-consumer/src/xcm_config.rs | 15 ++--- dip-template/runtimes/dip-provider/src/dip.rs | 11 ++-- dip-template/runtimes/xcm-tests/Cargo.toml | 1 + dip-template/runtimes/xcm-tests/src/para.rs | 52 ++++++++++++++-- dip-template/runtimes/xcm-tests/src/tests.rs | 29 ++++++--- pallets/pallet-dip-provider/src/lib.rs | 18 ++++-- pallets/pallet-dip-provider/src/traits.rs | 51 ++++++++++++---- 9 files changed, 173 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c064cc994..f9b91240db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2522,6 +2522,7 @@ dependencies = [ "sp-runtime", "sp-std", "xcm", + "xcm-builder", "xcm-emulator", "xcm-executor", ] diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index ef8146aae4..5fd113de08 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -16,16 +16,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use cumulus_primitives_core::{Junction::AccountKey20, OriginKind, ParaId}; +use cumulus_primitives_core::{Junction::AccountKey20, OriginKind}; use frame_support::ensure; use sp_core::Get; use sp_std::marker::PhantomData; use xcm::latest::{ Instruction, - Instruction::{BuyExecution, DescendOrigin, Transact, WithdrawAsset}, + Instruction::{BuyExecution, DepositAsset, DescendOrigin, RefundSurplus, Transact, WithdrawAsset}, Junction::{AccountId32, Parachain}, Junctions::{X1, X2}, - MultiLocation, Weight, + MultiLocation, ParentThen, Weight, }; use xcm_executor::traits::{ConvertOrigin, ShouldExecute}; @@ -36,7 +36,7 @@ pub struct AllowParachainProviderAsSubaccount(PhantomData ShouldExecute for AllowParachainProviderAsSubaccount where - ProviderParaId: Get, + ProviderParaId: Get, { fn should_execute( origin: &MultiLocation, @@ -45,19 +45,34 @@ where _weight_credit: &mut Weight, ) -> Result<(), ()> { // Ensure that the origin is a parachain allowed to act as identity provider. - ensure!(*origin == Parachain(ProviderParaId::get().into()).into(), ()); + ensure!( + *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), + () + ); + #[cfg(feature = "std")] + println!("{:?}", instructions); let mut iter = instructions.iter(); // This must match the implementation of the `IdentityProofDispatcher` trait. // TODO: Refactor so that they depend on each other and we avoid duplication - match (iter.next(), iter.next(), iter.next(), iter.next(), iter.next()) { + match ( + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + ) { ( - Some(DescendOrigin(X1(AccountId32 { .. } | AccountKey20 { .. }))), + Some(DescendOrigin(X1(AccountId32 { .. }))), Some(WithdrawAsset { .. }), Some(BuyExecution { .. }), Some(Transact { origin_kind: OriginKind::Native, .. }), + Some(RefundSurplus), + Some(DepositAsset { .. }), None, ) => Ok(()), _ => Err(()), @@ -72,7 +87,7 @@ pub struct OrElseCheckForParachainProvider(PhantomData< impl ShouldExecute for OrElseCheckForParachainProvider where Barrier: ShouldExecute, - ProviderParaId: Get, + ProviderParaId: Get, { fn should_execute( origin: &MultiLocation, @@ -80,30 +95,34 @@ where max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) + // TODO: This might not be correct, if the barrier wants to explicitely fail. + // Maybe this struct should be split into two, one where it fails if the barrier + // fails, and another one which tries the new barrier if the old one fails. + Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + }) } } -pub struct AccountIdJunctionToParachainOriginConverter( +pub struct AccountIdJunctionAsParachain( PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, ); impl ConvertOrigin - for AccountIdJunctionToParachainOriginConverter + for AccountIdJunctionAsParachain where - ProviderParaId: Get, - ParachainOrigin: From, + ProviderParaId: Get, + ParachainOrigin: From, RuntimeOrigin: From, { fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { let origin = origin.into(); - let provider_para_id: u32 = ProviderParaId::get().into(); + let provider_para_id = ProviderParaId::get(); match (kind, origin) { ( OriginKind::Native, @@ -111,7 +130,7 @@ where parents: 1, interior: X2(Parachain(para_id), AccountId32 { .. } | AccountKey20 { .. }), }, - ) if para_id == provider_para_id => Ok(ParachainOrigin::from(ProviderParaId::get()).into()), + ) if para_id == provider_para_id => Ok(ParachainOrigin::from(provider_para_id).into()), _ => Err(origin), } } diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs index bb7775be4f..9dd7b3c7a1 100644 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ b/dip-template/runtimes/dip-consumer/src/xcm_config.rs @@ -19,16 +19,16 @@ use cumulus_primitives_utility::ParentAsUmp; use frame_support::{ parameter_types, - traits::{ConstU32, Contains, Everything, Nothing}, + traits::{ConstU32, Contains, Nothing}, weights::{IdentityFee, Weight}, }; use frame_system::EnsureRoot; +use kilt_dip_support::xcm::{AccountIdJunctionAsParachain, OrElseCheckForParachainProvider}; use pallet_xcm::TestWeightInfo; -use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ - AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedToAccountId32, UsingComponents, + Account32Hash, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, + SignedToAccountId32, UsingComponents, }; use xcm_executor::XcmExecutor; @@ -39,12 +39,13 @@ use crate::{ parameter_types! { pub HereLocation: MultiLocation = MultiLocation::here(); + pub NoneNetworkId: Option = None; pub UnitWeightCost: Weight = Weight::from_ref_time(1_000); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); } -pub type Barrier = AllowTopLevelPaidExecutionFrom; -pub type AssetTransactorLocationConverter = SiblingParachainConvertsVia; +pub type Barrier = OrElseCheckForParachainProvider, ConstU32<2_000>>; +pub type AssetTransactorLocationConverter = Account32Hash; pub type LocalAssetTransactor = CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; pub type XcmRouter = (ParentAsUmp, XcmpQueue); @@ -74,7 +75,7 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = (); type MaxAssetsIntoHolding = ConstU32<64>; type MessageExporter = (); - type OriginConverter = SiblingParachainAsNative; + type OriginConverter = AccountIdJunctionAsParachain, cumulus_pallet_xcm::Origin, RuntimeOrigin>; type PalletInstancesInfo = AllPalletsWithSystem; type ResponseHandler = (); type RuntimeCall = RuntimeCall; diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index b8559c997c..52e34cfb67 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -16,16 +16,14 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::EnsureDidOrigin; use dip_support::IdentityDetailsAction; -use frame_support::traits::EitherOfDiverse; -use frame_system::EnsureRoot; +use frame_system::EnsureSigned; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; +use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, UniversalLocation, XcmRouter}; #[derive(Encode, Decode)] enum ConsumerParachainCalls { @@ -56,9 +54,10 @@ impl TxBuilder for ConsumerParachainTxBuilder { } impl pallet_dip_provider::Config for Runtime { - type CommitOrigin = EitherOfDiverse, EnsureDidOrigin>; + type CommitOriginCheck = EnsureSigned; + type CommitOrigin = AccountId; type Identifier = DidIdentifier; - type IdentityProofDispatcher = XcmRouterDispatcher; + type IdentityProofDispatcher = XcmRouterDispatcher; type IdentityProofGenerator = DidMerkleRootGenerator; type IdentityProvider = LinkedDidInfoProviderOf; type ProofOutput = Hash; diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index ae3b735c77..a6bcd85a28 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -35,6 +35,7 @@ sp-io = { workspace = true, features = ["std"] } sp-runtime = { workspace = true, features = ["std"] } sp-std = { workspace = true, features = ["std"] } xcm = { workspace = true, features = ["std"] } +xcm-builder = { workspace = true, features = ["std"] } xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } xcm-executor = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index c671ae0c83..bab29b3e97 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -25,7 +25,7 @@ pub(super) mod provider { pub(crate) use dip_provider_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; use did::did_details::{DidDetails, DidEncryptionKey, DidVerificationKey}; - use dip_provider_runtime_template::{AccountId, Balance, BlockNumber, System, Web3Name}; + use dip_provider_runtime_template::{AccountId, Balance, BlockNumber, System, Web3Name, UNIT}; use kilt_support::deposit::Deposit; use pallet_did_lookup::{linkable_account::LinkableAccountId, ConnectionRecord}; use pallet_web3_names::web3_name::Web3NameOwnership; @@ -38,6 +38,8 @@ pub(super) mod provider { use super::*; pub const PARA_ID: u32 = 2_000; + pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([190u8; 32]); + const INITIAL_BALANCE: Balance = 100_000 * UNIT; pub(crate) fn did_auth_key() -> ed25519::Pair { ed25519::Pair::from_seed(&[200u8; 32]) @@ -81,6 +83,12 @@ pub(super) mod provider { >::assimilate_storage(¶chain_info_config, &mut t) .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(DISPATCHER_ACCOUNT, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = TestExternalities::new(t); let did: DidIdentifier = did_auth_key().public().into(); let details = generate_did_details(); @@ -137,7 +145,11 @@ pub(super) mod consumer { }; use dip_consumer_runtime_template::System; - use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; + use xcm::latest::{ + Junction::{AccountId32, Parachain}, + Junctions::X2, + ParentThen, + }; use xcm_executor::traits::Convert; use super::*; @@ -146,9 +158,18 @@ pub(super) mod consumer { pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([90u8; 32]); const INITIAL_BALANCE: Balance = 100_000 * UNIT; - pub(crate) fn provider_parachain_account() -> AccountId { - AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(provider::PARA_ID))).into()) - .expect("Conversion of account from provider parachain to consumer parachain should not fail.") + pub(crate) fn provider_dispatcher_account_on_consumer() -> AccountId { + AssetTransactorLocationConverter::convert( + ParentThen(X2( + Parachain(provider::PARA_ID), + AccountId32 { + network: None, + id: provider::DISPATCHER_ACCOUNT.into(), + }, + )) + .into(), + ) + .expect("Conversion of account from provider parachain to consumer parachain should not fail.") } pub(crate) fn para_ext() -> TestExternalities { @@ -165,7 +186,7 @@ pub(super) mod consumer { pallet_balances::GenesisConfig:: { balances: vec![ - (provider_parachain_account(), INITIAL_BALANCE), + (provider_dispatcher_account_on_consumer(), INITIAL_BALANCE), (DISPATCHER_ACCOUNT, INITIAL_BALANCE), ], } @@ -178,4 +199,23 @@ pub(super) mod consumer { }); ext } + + #[cfg(test)] + pub(crate) use test_utils::*; + + #[cfg(test)] + mod test_utils { + use super::*; + + use polkadot_parachain::primitives::Sibling; + use xcm::latest::Junctions::X1; + use xcm_builder::SiblingParachainConvertsVia; + + pub(crate) fn provider_parachain_account_on_consumer() -> AccountId { + SiblingParachainConvertsVia::::convert( + ParentThen(X1(Parachain(provider::PARA_ID))).into(), + ) + .expect("Conversion of account from provider parachain to consumer parachain should not fail.") + } + } } diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 62114f0fee..14acc2c32f 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -16,6 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use crate::para::consumer::{provider_dispatcher_account_on_consumer, provider_parachain_account_on_consumer}; + use super::*; use did::{Did, DidSignature}; @@ -43,8 +45,8 @@ use xcm_emulator::TestExt; use cumulus_pallet_xcmp_queue::Event as XcmpEvent; use dip_consumer_runtime_template::{ - BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, - RuntimeEvent, System, + Balances, BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, + RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, }; use dip_provider_runtime_template::{DipProvider, Runtime as ProviderRuntime}; @@ -53,11 +55,15 @@ fn commit_identity() { Network::reset(); let did: DidIdentifier = para::provider::did_auth_key().public().into(); + let provider_parachain_on_consumer_parachain_balance_before = + ConsumerParachain::execute_with(|| Balances::free_balance(provider_parachain_account_on_consumer())); + let dispatcher_on_consumer_parachain_balance_before = + ConsumerParachain::execute_with(|| Balances::free_balance(provider_dispatcher_account_on_consumer())); // 1. Send identity commitment from DIP provider to DIP consumer. ProviderParachain::execute_with(|| { assert_ok!(DipProvider::commit_identity( - RawOrigin::Root.into(), + RawOrigin::Signed(para::provider::DISPATCHER_ACCOUNT).into(), did.clone(), Box::new(ParentThen(X1(Parachain(para::consumer::PARA_ID))).into()), Box::new((Here, 1_000_000_000).into()), @@ -77,15 +83,24 @@ fn commit_identity() { ))); // 2.2 Verify the proof digest was stored correctly. assert!(DipConsumer::identity_proofs(&did).is_some()); + // 2.3 Verify that the provider parachain sovereign account balance has not + // changed. + let provider_parachain_on_consumer_parachain_balance_after = + Balances::free_balance(provider_parachain_account_on_consumer()); + assert_eq!( + provider_parachain_on_consumer_parachain_balance_before, + provider_parachain_on_consumer_parachain_balance_after + ); + // 2.4 Verify that the dispatcher's account balance on the consumer parachain + // has decreased. + let dispatcher_on_consumer_parachain_balance_after = + Balances::free_balance(provider_dispatcher_account_on_consumer()); + assert!(dispatcher_on_consumer_parachain_balance_after < dispatcher_on_consumer_parachain_balance_before); }); // 3. Call an extrinsic on the consumer chain with a valid proof and signature let did_details = ProviderParachain::execute_with(|| { Did::get(&did).expect("DID details should be stored on the provider chain.") }); - println!( - "Complete DID details encoded size: {:?} bytes", - did_details.encoded_size() - ); let (web3_name, ownership_details) = ProviderParachain::execute_with(|| { let web3_name = Names::::get(&did).expect("Web3name should be linked to the DID on the provider chain."); diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index cf2983fa2d..a6a11abddb 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -44,14 +44,15 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type CommitOrigin: EnsureOrigin; + type CommitOriginCheck: EnsureOrigin; + type CommitOrigin: Into; type Identifier: Parameter; type IdentityProofGenerator: IdentityProofGenerator< Self::Identifier, IdentityOf, Output = Self::ProofOutput, >; - type IdentityProofDispatcher: IdentityProofDispatcher; + type IdentityProofDispatcher: IdentityProofDispatcher; type IdentityProvider: IdentityProvider; type ProofOutput: Clone + Eq + Debug; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -91,7 +92,7 @@ pub mod pallet { weight: Weight, ) -> DispatchResult { // TODO: Charge the dispatcher based on the destination weight configuration - T::CommitOrigin::ensure_origin(origin)?; + let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.into())?; let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { @@ -107,9 +108,14 @@ pub mod pallet { let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; - let (ticket, _) = - T::IdentityProofDispatcher::pre_dispatch::(action.clone(), asset, weight, destination) - .map_err(|_| Error::::Predispatch)?; + let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( + action.clone(), + dispatcher, + asset, + weight, + destination, + ) + .map_err(|_| Error::::Predispatch)?; // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the // fee, in addition to the cost on the target chain. diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 12784472e4..688e2dbbdd 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -52,16 +52,17 @@ pub use identity_dispatch::*; pub mod identity_dispatch { use super::*; - use frame_support::weights::Weight; + use frame_support::{pallet_prelude::Get, weights::Weight}; use parity_scale_codec::Encode; - use sp_std::{marker::PhantomData, vec}; + use sp_std::marker::PhantomData; - pub trait IdentityProofDispatcher { + pub trait IdentityProofDispatcher { type PreDispatchOutput; type Error; fn pre_dispatch>( action: IdentityDetailsAction, + source: AccountId, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -73,14 +74,15 @@ pub mod identity_dispatch { // Returns `Ok` without doing anything. pub struct NullIdentityProofDispatcher; - impl IdentityProofDispatcher - for NullIdentityProofDispatcher + impl + IdentityProofDispatcher for NullIdentityProofDispatcher { type PreDispatchOutput = (); type Error = (); fn pre_dispatch<_B>( _action: IdentityDetailsAction, + _source: AccountId, _asset: MultiAsset, _weight: Weight, _destination: MultiLocation, @@ -96,22 +98,24 @@ pub mod identity_dispatch { // Dispatcher using a type implementing the `SendXcm` trait. // It properly encodes the `Transact` operation, then delegates everything else // to the sender, similarly to what the XCM pallet's `send` extrinsic does. - pub struct XcmRouterDispatcher( - PhantomData<(Router, Identifier, ProofOutput, Details)>, - ); + pub struct XcmRouterDispatcher(PhantomData<(Router, UniversalLocationProvider)>); - impl IdentityProofDispatcher - for XcmRouterDispatcher + impl + IdentityProofDispatcher + for XcmRouterDispatcher where Router: SendXcm, + UniversalLocationProvider: Get, Identifier: Encode, ProofOutput: Encode, + AccountId: Into<[u8; 32]> + Clone, { type PreDispatchOutput = Router::Ticket; type Error = SendError; fn pre_dispatch>( action: IdentityDetailsAction, + source: AccountId, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -123,18 +127,39 @@ pub mod identity_dispatch { // TODO: Set an error handler and an appendix to refund any leftover funds to // the provider parachain sovereign account. - let operation = [vec![ + let operation = [[ + DescendOrigin(X1(AccountId32 { + network: None, + id: source.clone().into(), + })), WithdrawAsset(asset.clone().into()), BuyExecution { fees: asset, - // TODO: Configurable weight limit? - weight_limit: Unlimited, + weight_limit: Limited(weight), }, Transact { origin_kind: OriginKind::Native, require_weight_at_most: weight, call: dest_tx, }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: MultiLocation { + parents: 1, + // TODO: Error handling + interior: Here + .into_location() + .reanchored(&destination, UniversalLocationProvider::get()) + .unwrap() + .pushed_with_interior(AccountId32 { + network: None, + id: source.into(), + }) + .unwrap() + .interior, + }, + }, ]] .concat(); let op = Xcm(operation); From 31c3015b99c7544d650614718d1ec9cc06dd08c1 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 1 Jun 2023 15:47:56 +0200 Subject: [PATCH 03/13] Cleanups --- dip-template/runtimes/xcm-tests/src/tests.rs | 4 ++++ pallets/pallet-dip-provider/src/lib.rs | 3 +-- pallets/pallet-dip-provider/src/traits.rs | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 14acc2c32f..1f86c24631 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -101,6 +101,10 @@ fn commit_identity() { let did_details = ProviderParachain::execute_with(|| { Did::get(&did).expect("DID details should be stored on the provider chain.") }); + println!( + "Complete DID details encoded size: {:?} bytes", + did_details.encoded_size() + ); let (web3_name, ownership_details) = ProviderParachain::execute_with(|| { let web3_name = Names::::get(&did).expect("Web3name should be linked to the DID on the provider chain."); diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index a6a11abddb..91bca28e4a 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -91,7 +91,6 @@ pub mod pallet { asset: Box, weight: Weight, ) -> DispatchResult { - // TODO: Charge the dispatcher based on the destination weight configuration let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.into())?; let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; @@ -118,7 +117,7 @@ pub mod pallet { .map_err(|_| Error::::Predispatch)?; // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the - // fee, in addition to the cost on the target chain. + // fee. T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; Self::deposit_event(Event::IdentityInfoDispatched(action, Box::new(destination))); diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 688e2dbbdd..9f5b40382d 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -147,6 +147,7 @@ pub mod identity_dispatch { assets: Wild(All), beneficiary: MultiLocation { parents: 1, + // Re-anchor the same account junction as seen from the destination. // TODO: Error handling interior: Here .into_location() From de7f378487c87e44b57e3dbe0897542cf57e4439 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 08:45:02 +0200 Subject: [PATCH 04/13] Rename barrier adapters --- crates/kilt-dip-support/src/xcm.rs | 63 +++++++++---------- .../runtimes/dip-consumer/src/xcm_config.rs | 4 +- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index 5fd113de08..b98c5621ae 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -49,8 +49,6 @@ where *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), () ); - #[cfg(feature = "std")] - println!("{:?}", instructions); let mut iter = instructions.iter(); // This must match the implementation of the `IdentityProofDispatcher` trait. // TODO: Refactor so that they depend on each other and we avoid duplication @@ -80,11 +78,11 @@ where } } -// Decorate an existing Barrier to add one more check to allow a sibling -// parachain as the DIP provider. -pub struct OrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); +// Decorate an existing barrier to add one more check in case all the previous +// barriers fail. +pub struct OkOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); -impl ShouldExecute for OrElseCheckForParachainProvider +impl ShouldExecute for OkOrElseCheckForParachainProvider where Barrier: ShouldExecute, ProviderParaId: Get, @@ -95,9 +93,6 @@ where max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { - // TODO: This might not be correct, if the barrier wants to explicitely fail. - // Maybe this struct should be split into two, one where it fails if the barrier - // fails, and another one which tries the new barrier if the old one fails. Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { AllowParachainProviderAsSubaccount::::should_execute( origin, @@ -109,6 +104,31 @@ where } } +// Decorate an existing barrier to check for the provider parachain origin only +// in case none of the previous barriers fail. +pub struct OkAndThenCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); + +impl ShouldExecute for OkAndThenCheckForParachainProvider +where + Barrier: ShouldExecute, + ProviderParaId: Get, +{ + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + } +} + pub struct AccountIdJunctionAsParachain( PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, ); @@ -135,28 +155,3 @@ where } } } - -// // Decorate an existing OriginConverter to add the conversion of a sibling -// // parachain as the DIP provider. -// pub struct OrElseSiblingParachainProviderConverter( -// PhantomData<(OriginConverter, ProviderParaId, ParachainOrigin, -// RuntimeOrigin)>, ); - -// impl -// ConvertOrigin -// for OrElseSiblingParachainProviderConverter where -// OriginConverter: ConvertOrigin, -// ProviderParaId: Get, -// ParachainOrigin: From, -// RuntimeOrigin: From, -// { -// fn convert_origin(origin: impl Into, kind: OriginKind) -> -// Result { -// OriginConverter::convert_origin(origin, kind)?; -// AccountIdJunctionToParachainOriginConverter::::convert_origin( origin, kind, -// ) -// } -// } diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs index 9dd7b3c7a1..11ae2a266e 100644 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ b/dip-template/runtimes/dip-consumer/src/xcm_config.rs @@ -23,7 +23,7 @@ use frame_support::{ weights::{IdentityFee, Weight}, }; use frame_system::EnsureRoot; -use kilt_dip_support::xcm::{AccountIdJunctionAsParachain, OrElseCheckForParachainProvider}; +use kilt_dip_support::xcm::{AccountIdJunctionAsParachain, OkOrElseCheckForParachainProvider}; use pallet_xcm::TestWeightInfo; use xcm::latest::prelude::*; use xcm_builder::{ @@ -44,7 +44,7 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); } -pub type Barrier = OrElseCheckForParachainProvider, ConstU32<2_000>>; +pub type Barrier = OkOrElseCheckForParachainProvider, ConstU32<2_000>>; pub type AssetTransactorLocationConverter = Account32Hash; pub type LocalAssetTransactor = CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; From a32fdc49116afc6a452b3123499219ec3f123b04 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 10:30:39 +0200 Subject: [PATCH 05/13] Compiling! --- Cargo.lock | 1 + pallets/pallet-dip-provider/Cargo.toml | 3 +++ pallets/pallet-dip-provider/src/lib.rs | 6 +++--- pallets/pallet-dip-provider/src/traits.rs | 26 +++++++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9b91240db..c1b42a0a40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6285,6 +6285,7 @@ dependencies = [ name = "pallet-dip-provider" version = "1.11.0-dev" dependencies = [ + "did", "dip-support", "frame-support", "frame-system", diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 6283ad284a..e87da12329 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -14,6 +14,7 @@ version.workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +did.workspace = true dip-support.workspace = true frame-support.workspace = true frame-system.workspace = true @@ -25,6 +26,7 @@ xcm.workspace = true [features] default = ["std"] std = [ + "did/std", "dip-support/std", "frame-support/std", "frame-system/std", @@ -34,6 +36,7 @@ std = [ "xcm/std", ] runtime-benchmarks = [ + "did/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks" ] diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 91bca28e4a..34ca55f400 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -35,7 +35,7 @@ pub mod pallet { use dip_support::IdentityDetailsAction; - use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; + use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, SubmitterInfo, TxBuilder}; pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; pub type IdentityProofActionOf = IdentityDetailsAction<::Identifier, ::ProofOutput>; @@ -45,7 +45,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type CommitOriginCheck: EnsureOrigin; - type CommitOrigin: Into; + type CommitOrigin: SubmitterInfo; type Identifier: Parameter; type IdentityProofGenerator: IdentityProofGenerator< Self::Identifier, @@ -91,7 +91,7 @@ pub mod pallet { asset: Box, weight: Weight, ) -> DispatchResult { - let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.into())?; + let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.submitter())?; let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 9f5b40382d..b61f792d65 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -16,6 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use did::DidRawOrigin; use dip_support::IdentityDetailsAction; use xcm::{latest::prelude::*, DoubleEncoded}; @@ -222,3 +223,28 @@ pub trait TxBuilder { action: IdentityDetailsAction, ) -> Result, Self::Error>; } + +pub trait SubmitterInfo { + type Submitter; + + fn submitter(&self) -> Self::Submitter; +} + +impl SubmitterInfo for frame_support::sp_runtime::AccountId32 { + type Submitter = Self; + + fn submitter(&self) -> Self::Submitter { + self.clone() + } +} + +impl SubmitterInfo for DidRawOrigin +where + AccountId: Clone, +{ + type Submitter = AccountId; + + fn submitter(&self) -> Self::Submitter { + self.submitter.clone() + } +} From 7611efa6022bb202003026c5b48bc4e0d55c9bcc Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 10:38:38 +0200 Subject: [PATCH 06/13] Switch signed origin to DID origin --- dip-template/runtimes/dip-provider/src/dip.rs | 6 +++--- dip-template/runtimes/xcm-tests/src/tests.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 52e34cfb67..f60ec65040 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -16,8 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use did::{DidRawOrigin, EnsureDidOrigin}; use dip_support::IdentityDetailsAction; -use frame_system::EnsureSigned; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; @@ -54,8 +54,8 @@ impl TxBuilder for ConsumerParachainTxBuilder { } impl pallet_dip_provider::Config for Runtime { - type CommitOriginCheck = EnsureSigned; - type CommitOrigin = AccountId; + type CommitOriginCheck = EnsureDidOrigin; + type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; type IdentityProofDispatcher = XcmRouterDispatcher; type IdentityProofGenerator = DidMerkleRootGenerator; diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 1f86c24631..0d9845aeff 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -20,7 +20,7 @@ use crate::para::consumer::{provider_dispatcher_account_on_consumer, provider_pa use super::*; -use did::{Did, DidSignature}; +use did::{Did, DidRawOrigin, DidSignature}; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use kilt_dip_support::{ @@ -63,7 +63,11 @@ fn commit_identity() { // 1. Send identity commitment from DIP provider to DIP consumer. ProviderParachain::execute_with(|| { assert_ok!(DipProvider::commit_identity( - RawOrigin::Signed(para::provider::DISPATCHER_ACCOUNT).into(), + DidRawOrigin { + id: did.clone(), + submitter: para::provider::DISPATCHER_ACCOUNT + } + .into(), did.clone(), Box::new(ParentThen(X1(Parachain(para::consumer::PARA_ID))).into()), Box::new((Here, 1_000_000_000).into()), From e3c81acfff50f084a7028f9df6947ea2b16b6fde Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 11:00:21 +0200 Subject: [PATCH 07/13] Refactoring --- Cargo.lock | 3 +- crates/kilt-dip-support/Cargo.toml | 8 +- crates/kilt-dip-support/src/xcm.rs | 102 ++++++++++++++++-- .../runtimes/dip-consumer/src/xcm_config.rs | 2 +- dip-template/runtimes/dip-provider/Cargo.toml | 3 + dip-template/runtimes/dip-provider/src/dip.rs | 3 +- .../runtimes/dip-provider/src/xcm_config.rs | 2 +- dip-template/runtimes/xcm-tests/src/para.rs | 4 +- dip-template/runtimes/xcm-tests/src/tests.rs | 2 +- pallets/pallet-dip-provider/src/traits.rs | 81 +------------- runtimes/common/src/xcm_config.rs | 2 +- runtimes/peregrine/src/xcm_config.rs | 2 +- runtimes/spiritnet/src/xcm_config.rs | 2 +- 13 files changed, 113 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1b42a0a40..0558f28fdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2450,6 +2450,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-dip-support", "kilt-runtime-api-dip-provider", "pallet-aura", "pallet-authorship", @@ -4221,8 +4222,8 @@ dependencies = [ name = "kilt-dip-support" version = "1.11.0-dev" dependencies = [ - "cumulus-primitives-core", "did", + "dip-support", "frame-support", "frame-system", "pallet-dip-consumer", diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index aed093c72d..b417b44cba 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true [dependencies] # Internal dependencies did.workspace = true +dip-support.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true @@ -32,13 +33,11 @@ sp-std.workspace = true xcm.workspace = true xcm-executor.workspace = true -# Cumulus dependencies -cumulus-primitives-core.workspace = true - [features] default = ["std"] std = [ "did/std", + "dip-support/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", "parity-scale-codec/std", @@ -50,7 +49,6 @@ std = [ "sp-trie/std", "sp-std/std", "xcm-executor/std", - "xcm/std", - "cumulus-primitives-core/std" + "xcm/std" ] runtime-benchmarks = [] diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index b98c5621ae..0334d6a610 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -16,22 +16,31 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use cumulus_primitives_core::{Junction::AccountKey20, OriginKind}; +use dip_support::IdentityDetailsAction; use frame_support::ensure; +use pallet_dip_provider::traits::{IdentityProofDispatcher, TxBuilder}; +use parity_scale_codec::Encode; use sp_core::Get; use sp_std::marker::PhantomData; -use xcm::latest::{ +use xcm::v3::{ Instruction, Instruction::{BuyExecution, DepositAsset, DescendOrigin, RefundSurplus, Transact, WithdrawAsset}, - Junction::{AccountId32, Parachain}, - Junctions::{X1, X2}, - MultiLocation, ParentThen, Weight, + InteriorMultiLocation, + Junction::{AccountId32, AccountKey20, Parachain}, + Junctions::{Here, X1, X2}, + MultiAsset, + MultiAssetFilter::Wild, + MultiAssets, MultiLocation, OriginKind, ParentThen, SendError, SendXcm, Weight, + WeightLimit::Limited, + WildMultiAsset::All, + Xcm, }; use xcm_executor::traits::{ConvertOrigin, ShouldExecute}; -// Allows a parachain to descend to an `X1(AccountId32)` or `X1(AccountId20)` -// junction, withdraw fees from their balance, and then carry on with a -// `Transact`. +// Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees +// from their balance, and then carry on with a `Transact`. +// Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` +// origin converter. pub struct AllowParachainProviderAsSubaccount(PhantomData); impl ShouldExecute for AllowParachainProviderAsSubaccount @@ -155,3 +164,80 @@ where } } } + +// Dispatcher using a type implementing the `SendXcm` trait. +// It properly encodes the `Transact` operation, then delegates everything else +// to the sender, similarly to what the XCM pallet's `send` extrinsic does. +pub struct XcmRouterDispatcher(PhantomData<(Router, UniversalLocationProvider)>); + +impl + IdentityProofDispatcher + for XcmRouterDispatcher +where + Router: SendXcm, + UniversalLocationProvider: Get, + Identifier: Encode, + ProofOutput: Encode, + AccountId: Into<[u8; 32]> + Clone, +{ + type PreDispatchOutput = Router::Ticket; + type Error = SendError; + + fn pre_dispatch>( + action: IdentityDetailsAction, + source: AccountId, + asset: MultiAsset, + weight: Weight, + destination: MultiLocation, + ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { + // TODO: Replace with proper error handling + let dest_tx = Builder::build(destination, action) + .map_err(|_| ()) + .expect("Failed to build call"); + + // TODO: Set an error handler and an appendix to refund any leftover funds to + // the provider parachain sovereign account. + let operation = [[ + DescendOrigin(X1(AccountId32 { + network: None, + id: source.clone().into(), + })), + WithdrawAsset(asset.clone().into()), + BuyExecution { + fees: asset, + weight_limit: Limited(weight), + }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: weight, + call: dest_tx, + }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: MultiLocation { + parents: 1, + // Re-anchor the same account junction as seen from the destination. + // TODO: Error handling + interior: Here + .into_location() + .reanchored(&destination, UniversalLocationProvider::get()) + .unwrap() + .pushed_with_interior(AccountId32 { + network: None, + id: source.into(), + }) + .unwrap() + .interior, + }, + }, + ]] + .concat(); + let op = Xcm(operation); + Router::validate(&mut Some(destination), &mut Some(op)) + } + + fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { + Router::deliver(pre_output).map(|_| ()) + } +} diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs index 11ae2a266e..f532c358d5 100644 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ b/dip-template/runtimes/dip-consumer/src/xcm_config.rs @@ -25,7 +25,7 @@ use frame_support::{ use frame_system::EnsureRoot; use kilt_dip_support::xcm::{AccountIdJunctionAsParachain, OkOrElseCheckForParachainProvider}; use pallet_xcm::TestWeightInfo; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{ Account32Hash, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, SignedToAccountId32, UsingComponents, diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index ce1a7582bb..14fdeb6257 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -20,6 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true dip-support.workspace = true +kilt-dip-support.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-did-lookup.workspace = true pallet-dip-provider.workspace = true @@ -78,6 +79,7 @@ std = [ "scale-info/std", "did/std", "dip-support/std", + "kilt-dip-support/std", "kilt-runtime-api-dip-provider/std", "pallet-did-lookup/std", "pallet-dip-provider/std", @@ -123,6 +125,7 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", + "kilt-dip-support/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", "pallet-web3-names/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index f60ec65040..dc137bf687 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -18,7 +18,8 @@ use did::{DidRawOrigin, EnsureDidOrigin}; use dip_support::IdentityDetailsAction; -use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; +use kilt_dip_support::xcm::XcmRouterDispatcher; +use pallet_dip_provider::traits::TxBuilder; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; diff --git a/dip-template/runtimes/dip-provider/src/xcm_config.rs b/dip-template/runtimes/dip-provider/src/xcm_config.rs index d73c126189..df62925d53 100644 --- a/dip-template/runtimes/dip-provider/src/xcm_config.rs +++ b/dip-template/runtimes/dip-provider/src/xcm_config.rs @@ -24,7 +24,7 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::TestWeightInfo; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, SignedToAccountId32, UsingComponents}; use xcm_executor::XcmExecutor; diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index bab29b3e97..a24076fe04 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -145,7 +145,7 @@ pub(super) mod consumer { }; use dip_consumer_runtime_template::System; - use xcm::latest::{ + use xcm::v3::{ Junction::{AccountId32, Parachain}, Junctions::X2, ParentThen, @@ -208,7 +208,7 @@ pub(super) mod consumer { use super::*; use polkadot_parachain::primitives::Sibling; - use xcm::latest::Junctions::X1; + use xcm::v3::Junctions::X1; use xcm_builder::SiblingParachainConvertsVia; pub(crate) fn provider_parachain_account_on_consumer() -> AccountId { diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 0d9845aeff..87fb7b6844 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -36,7 +36,7 @@ use runtime_common::dip::{ }; use sp_core::Pair; use sp_runtime::traits::Zero; -use xcm::latest::{ +use xcm::v3::{ Junction::Parachain, Junctions::{Here, X1}, ParentThen, diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index b61f792d65..f5a8836d1f 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -53,9 +53,7 @@ pub use identity_dispatch::*; pub mod identity_dispatch { use super::*; - use frame_support::{pallet_prelude::Get, weights::Weight}; - use parity_scale_codec::Encode; - use sp_std::marker::PhantomData; + use frame_support::weights::Weight; pub trait IdentityProofDispatcher { type PreDispatchOutput; @@ -95,83 +93,6 @@ pub mod identity_dispatch { Ok(()) } } - - // Dispatcher using a type implementing the `SendXcm` trait. - // It properly encodes the `Transact` operation, then delegates everything else - // to the sender, similarly to what the XCM pallet's `send` extrinsic does. - pub struct XcmRouterDispatcher(PhantomData<(Router, UniversalLocationProvider)>); - - impl - IdentityProofDispatcher - for XcmRouterDispatcher - where - Router: SendXcm, - UniversalLocationProvider: Get, - Identifier: Encode, - ProofOutput: Encode, - AccountId: Into<[u8; 32]> + Clone, - { - type PreDispatchOutput = Router::Ticket; - type Error = SendError; - - fn pre_dispatch>( - action: IdentityDetailsAction, - source: AccountId, - asset: MultiAsset, - weight: Weight, - destination: MultiLocation, - ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { - // TODO: Replace with proper error handling - let dest_tx = Builder::build(destination, action) - .map_err(|_| ()) - .expect("Failed to build call"); - - // TODO: Set an error handler and an appendix to refund any leftover funds to - // the provider parachain sovereign account. - let operation = [[ - DescendOrigin(X1(AccountId32 { - network: None, - id: source.clone().into(), - })), - WithdrawAsset(asset.clone().into()), - BuyExecution { - fees: asset, - weight_limit: Limited(weight), - }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: weight, - call: dest_tx, - }, - RefundSurplus, - DepositAsset { - assets: Wild(All), - beneficiary: MultiLocation { - parents: 1, - // Re-anchor the same account junction as seen from the destination. - // TODO: Error handling - interior: Here - .into_location() - .reanchored(&destination, UniversalLocationProvider::get()) - .unwrap() - .pushed_with_interior(AccountId32 { - network: None, - id: source.into(), - }) - .unwrap() - .interior, - }, - }, - ]] - .concat(); - let op = Xcm(operation); - Router::validate(&mut Some(destination), &mut Some(op)) - } - - fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { - Router::deliver(pre_output).map(|_| ()) - } - } } pub use identity_provision::*; diff --git a/runtimes/common/src/xcm_config.rs b/runtimes/common/src/xcm_config.rs index 51eed3b5f3..3ac0affa7b 100644 --- a/runtimes/common/src/xcm_config.rs +++ b/runtimes/common/src/xcm_config.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use frame_support::{log, match_types, parameter_types, weights::Weight}; use polkadot_parachain::primitives::Sibling; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{AccountId32Aliases, CurrencyAdapter, IsConcrete, ParentIsPreset, SiblingParachainConvertsVia}; use xcm_executor::traits::ShouldExecute; diff --git a/runtimes/peregrine/src/xcm_config.rs b/runtimes/peregrine/src/xcm_config.rs index a21d4e1049..4691ccedce 100644 --- a/runtimes/peregrine/src/xcm_config.rs +++ b/runtimes/peregrine/src/xcm_config.rs @@ -27,7 +27,7 @@ use frame_support::{ }; use pallet_xcm::XcmPassthrough; use sp_core::ConstU32; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{ AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, RelayChainAsNative, SiblingParachainAsNative, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, UsingComponents, WithComputedOrigin, diff --git a/runtimes/spiritnet/src/xcm_config.rs b/runtimes/spiritnet/src/xcm_config.rs index e399457755..840a2e48d0 100644 --- a/runtimes/spiritnet/src/xcm_config.rs +++ b/runtimes/spiritnet/src/xcm_config.rs @@ -27,7 +27,7 @@ use frame_support::{ }; use pallet_xcm::XcmPassthrough; use sp_core::ConstU32; -use xcm::latest::prelude::*; +use xcm::v3::prelude::*; use xcm_builder::{ AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, RelayChainAsNative, SiblingParachainAsNative, SignedAccountId32AsNative, SignedToAccountId32, UsingComponents, WithComputedOrigin, From 7c06647d72d5c62023d2b3c16aa96154a062981e Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 11:12:36 +0200 Subject: [PATCH 08/13] Move things inside the kilt-dip-support crate --- crates/kilt-dip-support/src/xcm.rs | 286 +++++++++--------- .../runtimes/dip-consumer/src/xcm_config.rs | 2 +- dip-template/runtimes/dip-provider/src/dip.rs | 4 +- 3 files changed, 153 insertions(+), 139 deletions(-) diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index 0334d6a610..5553ccb618 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -17,162 +17,33 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use dip_support::IdentityDetailsAction; -use frame_support::ensure; use pallet_dip_provider::traits::{IdentityProofDispatcher, TxBuilder}; use parity_scale_codec::Encode; use sp_core::Get; use sp_std::marker::PhantomData; use xcm::v3::{ - Instruction, Instruction::{BuyExecution, DepositAsset, DescendOrigin, RefundSurplus, Transact, WithdrawAsset}, InteriorMultiLocation, - Junction::{AccountId32, AccountKey20, Parachain}, - Junctions::{Here, X1, X2}, + Junction::AccountId32, + Junctions::{Here, X1}, MultiAsset, MultiAssetFilter::Wild, - MultiAssets, MultiLocation, OriginKind, ParentThen, SendError, SendXcm, Weight, + MultiAssets, MultiLocation, OriginKind, SendError, SendXcm, Weight, WeightLimit::Limited, WildMultiAsset::All, Xcm, }; -use xcm_executor::traits::{ConvertOrigin, ShouldExecute}; - -// Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees -// from their balance, and then carry on with a `Transact`. -// Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` -// origin converter. -pub struct AllowParachainProviderAsSubaccount(PhantomData); - -impl ShouldExecute for AllowParachainProviderAsSubaccount -where - ProviderParaId: Get, -{ - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - _max_weight: Weight, - _weight_credit: &mut Weight, - ) -> Result<(), ()> { - // Ensure that the origin is a parachain allowed to act as identity provider. - ensure!( - *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), - () - ); - let mut iter = instructions.iter(); - // This must match the implementation of the `IdentityProofDispatcher` trait. - // TODO: Refactor so that they depend on each other and we avoid duplication - match ( - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - ) { - ( - Some(DescendOrigin(X1(AccountId32 { .. }))), - Some(WithdrawAsset { .. }), - Some(BuyExecution { .. }), - Some(Transact { - origin_kind: OriginKind::Native, - .. - }), - Some(RefundSurplus), - Some(DepositAsset { .. }), - None, - ) => Ok(()), - _ => Err(()), - } - } -} - -// Decorate an existing barrier to add one more check in case all the previous -// barriers fail. -pub struct OkOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); - -impl ShouldExecute for OkOrElseCheckForParachainProvider -where - Barrier: ShouldExecute, - ProviderParaId: Get, -{ - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ()> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) - }) - } -} - -// Decorate an existing barrier to check for the provider parachain origin only -// in case none of the previous barriers fail. -pub struct OkAndThenCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); - -impl ShouldExecute for OkAndThenCheckForParachainProvider -where - Barrier: ShouldExecute, - ProviderParaId: Get, -{ - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ()> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) - } -} - -pub struct AccountIdJunctionAsParachain( - PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, -); - -impl ConvertOrigin - for AccountIdJunctionAsParachain -where - ProviderParaId: Get, - ParachainOrigin: From, - RuntimeOrigin: From, -{ - fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { - let origin = origin.into(); - let provider_para_id = ProviderParaId::get(); - match (kind, origin) { - ( - OriginKind::Native, - MultiLocation { - parents: 1, - interior: X2(Parachain(para_id), AccountId32 { .. } | AccountKey20 { .. }), - }, - ) if para_id == provider_para_id => Ok(ParachainOrigin::from(provider_para_id).into()), - _ => Err(origin), - } - } -} // Dispatcher using a type implementing the `SendXcm` trait. // It properly encodes the `Transact` operation, then delegates everything else // to the sender, similarly to what the XCM pallet's `send` extrinsic does. -pub struct XcmRouterDispatcher(PhantomData<(Router, UniversalLocationProvider)>); +pub struct XcmRouterIdentityDispatcher( + PhantomData<(Router, UniversalLocationProvider)>, +); impl IdentityProofDispatcher - for XcmRouterDispatcher + for XcmRouterIdentityDispatcher where Router: SendXcm, UniversalLocationProvider: Get, @@ -241,3 +112,146 @@ where Router::deliver(pre_output).map(|_| ()) } } + +pub mod barriers { + use super::*; + + use frame_support::ensure; + use xcm::v3::{Instruction, Junction::Parachain, ParentThen}; + use xcm_executor::traits::ShouldExecute; + + // Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees + // from their balance, and then carry on with a `Transact`. + // Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` + // origin converter. + pub struct AllowParachainProviderAsSubaccount(PhantomData); + + impl ShouldExecute for AllowParachainProviderAsSubaccount + where + ProviderParaId: Get, + { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + // Ensure that the origin is a parachain allowed to act as identity provider. + ensure!( + *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), + () + ); + let mut iter = instructions.iter(); + // This must match the implementation of the `IdentityProofDispatcher` trait. + // TODO: Refactor so that they depend on each other and we avoid duplication + match ( + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + ) { + ( + Some(DescendOrigin(X1(AccountId32 { .. }))), + Some(WithdrawAsset { .. }), + Some(BuyExecution { .. }), + Some(Transact { + origin_kind: OriginKind::Native, + .. + }), + Some(RefundSurplus), + Some(DepositAsset { .. }), + None, + ) => Ok(()), + _ => Err(()), + } + } + } + + // Decorate an existing barrier to add one more check in case all the previous + // barriers fail. + pub struct OkOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); + + impl ShouldExecute for OkOrElseCheckForParachainProvider + where + Barrier: ShouldExecute, + ProviderParaId: Get, + { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + }) + } + } + + // Decorate an existing barrier to check for the provider parachain origin only + // in case none of the previous barriers fail. + pub struct ErrOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); + + impl ShouldExecute for ErrOrElseCheckForParachainProvider + where + Barrier: ShouldExecute, + ProviderParaId: Get, + { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; + AllowParachainProviderAsSubaccount::::should_execute( + origin, + instructions, + max_weight, + weight_credit, + ) + } + } +} + +pub mod origins { + use super::*; + + use xcm::v3::{Junction::Parachain, Junctions::X2}; + use xcm_executor::traits::ConvertOrigin; + + pub struct AccountIdJunctionAsParachain( + PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, + ); + + impl ConvertOrigin + for AccountIdJunctionAsParachain + where + ProviderParaId: Get, + ParachainOrigin: From, + RuntimeOrigin: From, + { + fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { + let origin = origin.into(); + let provider_para_id = ProviderParaId::get(); + match (kind, origin) { + ( + OriginKind::Native, + MultiLocation { + parents: 1, + interior: X2(Parachain(para_id), AccountId32 { .. }), + }, + ) if para_id == provider_para_id => Ok(ParachainOrigin::from(provider_para_id).into()), + _ => Err(origin), + } + } + } +} diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs index f532c358d5..83d2918888 100644 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ b/dip-template/runtimes/dip-consumer/src/xcm_config.rs @@ -23,7 +23,7 @@ use frame_support::{ weights::{IdentityFee, Weight}, }; use frame_system::EnsureRoot; -use kilt_dip_support::xcm::{AccountIdJunctionAsParachain, OkOrElseCheckForParachainProvider}; +use kilt_dip_support::xcm::{barriers::OkOrElseCheckForParachainProvider, origins::AccountIdJunctionAsParachain}; use pallet_xcm::TestWeightInfo; use xcm::v3::prelude::*; use xcm_builder::{ diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index dc137bf687..59b6aedade 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -18,7 +18,7 @@ use did::{DidRawOrigin, EnsureDidOrigin}; use dip_support::IdentityDetailsAction; -use kilt_dip_support::xcm::XcmRouterDispatcher; +use kilt_dip_support::xcm::XcmRouterIdentityDispatcher; use pallet_dip_provider::traits::TxBuilder; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; @@ -58,7 +58,7 @@ impl pallet_dip_provider::Config for Runtime { type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; - type IdentityProofDispatcher = XcmRouterDispatcher; + type IdentityProofDispatcher = XcmRouterIdentityDispatcher; type IdentityProofGenerator = DidMerkleRootGenerator; type IdentityProvider = LinkedDidInfoProviderOf; type ProofOutput = Hash; From b69dba206e8980d47632b70f9c39e76403073f55 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 11:45:07 +0200 Subject: [PATCH 09/13] Add debug_assert for created instructions --- crates/kilt-dip-support/src/xcm.rs | 57 ++++++++++++++++-------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index 5553ccb618..e8df5865de 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -104,6 +104,9 @@ where }, ]] .concat(); + // TODO: Restructure the trait to be able to inject the [Instruction] provider, + // and unit test that. + debug_assert!(barriers::instruction_matcher(&operation).is_ok()); let op = Xcm(operation); Router::validate(&mut Some(destination), &mut Some(op)) } @@ -120,6 +123,33 @@ pub mod barriers { use xcm::v3::{Instruction, Junction::Parachain, ParentThen}; use xcm_executor::traits::ShouldExecute; + pub(crate) fn instruction_matcher(instructions: &[Instruction]) -> Result<(), ()> { + let mut iter = instructions.iter(); + match ( + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + iter.next(), + ) { + ( + Some(DescendOrigin(X1(AccountId32 { .. }))), + Some(WithdrawAsset { .. }), + Some(BuyExecution { .. }), + Some(Transact { + origin_kind: OriginKind::Native, + .. + }), + Some(RefundSurplus), + Some(DepositAsset { .. }), + None, + ) => Ok(()), + _ => Err(()), + } + } + // Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees // from their balance, and then carry on with a `Transact`. // Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` @@ -141,32 +171,7 @@ pub mod barriers { *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), () ); - let mut iter = instructions.iter(); - // This must match the implementation of the `IdentityProofDispatcher` trait. - // TODO: Refactor so that they depend on each other and we avoid duplication - match ( - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - ) { - ( - Some(DescendOrigin(X1(AccountId32 { .. }))), - Some(WithdrawAsset { .. }), - Some(BuyExecution { .. }), - Some(Transact { - origin_kind: OriginKind::Native, - .. - }), - Some(RefundSurplus), - Some(DepositAsset { .. }), - None, - ) => Ok(()), - _ => Err(()), - } + instruction_matcher(instructions) } } From d1943ae9577aef064d3d802d03f25d1b2ba93bec Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 2 Jun 2023 12:09:55 +0200 Subject: [PATCH 10/13] More refactoring --- dip-template/runtimes/xcm-tests/src/tests.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 87fb7b6844..102addf83b 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -16,8 +16,6 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use crate::para::consumer::{provider_dispatcher_account_on_consumer, provider_parachain_account_on_consumer}; - use super::*; use did::{Did, DidRawOrigin, DidSignature}; @@ -55,10 +53,12 @@ fn commit_identity() { Network::reset(); let did: DidIdentifier = para::provider::did_auth_key().public().into(); - let provider_parachain_on_consumer_parachain_balance_before = - ConsumerParachain::execute_with(|| Balances::free_balance(provider_parachain_account_on_consumer())); - let dispatcher_on_consumer_parachain_balance_before = - ConsumerParachain::execute_with(|| Balances::free_balance(provider_dispatcher_account_on_consumer())); + let provider_parachain_on_consumer_parachain_balance_before = ConsumerParachain::execute_with(|| { + Balances::free_balance(para::consumer::provider_parachain_account_on_consumer()) + }); + let dispatcher_on_consumer_parachain_balance_before = ConsumerParachain::execute_with(|| { + Balances::free_balance(para::consumer::provider_dispatcher_account_on_consumer()) + }); // 1. Send identity commitment from DIP provider to DIP consumer. ProviderParachain::execute_with(|| { @@ -90,7 +90,7 @@ fn commit_identity() { // 2.3 Verify that the provider parachain sovereign account balance has not // changed. let provider_parachain_on_consumer_parachain_balance_after = - Balances::free_balance(provider_parachain_account_on_consumer()); + Balances::free_balance(para::consumer::provider_parachain_account_on_consumer()); assert_eq!( provider_parachain_on_consumer_parachain_balance_before, provider_parachain_on_consumer_parachain_balance_after @@ -98,7 +98,7 @@ fn commit_identity() { // 2.4 Verify that the dispatcher's account balance on the consumer parachain // has decreased. let dispatcher_on_consumer_parachain_balance_after = - Balances::free_balance(provider_dispatcher_account_on_consumer()); + Balances::free_balance(para::consumer::provider_dispatcher_account_on_consumer()); assert!(dispatcher_on_consumer_parachain_balance_after < dispatcher_on_consumer_parachain_balance_before); }); // 3. Call an extrinsic on the consumer chain with a valid proof and signature From 8f4dbd027b6bd8e9afd6bee3c17b7c7a9ec19662 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 5 Jun 2023 15:29:41 +0200 Subject: [PATCH 11/13] Add (failing) test about user-dispatched XCM message --- Cargo.lock | 2 + crates/kilt-dip-support/src/xcm.rs | 5 + dip-template/runtimes/xcm-tests/Cargo.toml | 2 + dip-template/runtimes/xcm-tests/src/tests.rs | 100 +++++++++++++++++-- 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0558f28fdb..759e8c19fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2503,12 +2503,14 @@ dependencies = [ "did", "dip-consumer-runtime-template", "dip-provider-runtime-template", + "dip-support", "frame-support", "frame-system", "kilt-dip-support", "kilt-support", "pallet-balances", "pallet-did-lookup", + "pallet-dip-provider", "pallet-web3-names", "parachain-info", "parity-scale-codec", diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index e8df5865de..d2a17353e6 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -166,6 +166,11 @@ pub mod barriers { _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { + #[cfg(feature = "std")] + println!( + "AllowParachainProviderAsSubaccount::should_execute(origin = {:?}, instructions = {:?}", + origin, instructions + ); // Ensure that the origin is a parachain allowed to act as identity provider. ensure!( *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index a6bcd85a28..d922974440 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -15,12 +15,14 @@ cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } did = { workspace = true, features = ["std"] } dip-consumer-runtime-template = { workspace = true, features = ["std"] } dip-provider-runtime-template = { workspace = true, features = ["std"] } +dip-support = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } kilt-dip-support = { workspace = true, features = ["std"] } kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-did-lookup = { workspace = true, features = ["std"] } +pallet-dip-provider = { workspace = true, features = ["std"] } pallet-web3-names = { workspace = true, features = ["std"] } parachain-info = { workspace = true, features = ["std"] } parity-scale-codec = {workspace = true, features = ["std", "derive"]} diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 102addf83b..1e8a231166 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -19,6 +19,7 @@ use super::*; use did::{Did, DidRawOrigin, DidSignature}; +use dip_support::IdentityDetailsAction; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use kilt_dip_support::{ @@ -26,6 +27,7 @@ use kilt_dip_support::{ merkle::MerkleProof, }; use pallet_did_lookup::{linkable_account::LinkableAccountId, ConnectedAccounts}; +use pallet_dip_provider::traits::TxBuilder; use pallet_web3_names::{Names, Owner}; use parity_scale_codec::Encode; use runtime_common::dip::{ @@ -34,10 +36,19 @@ use runtime_common::dip::{ }; use sp_core::Pair; use sp_runtime::traits::Zero; -use xcm::v3::{ - Junction::Parachain, - Junctions::{Here, X1}, - ParentThen, +use xcm::{ + v3::{ + Instruction::{BuyExecution, DepositAsset, RefundSurplus, Transact, WithdrawAsset}, + Junction::{AccountId32, Parachain}, + Junctions::{Here, X1}, + MultiAsset, + MultiAssetFilter::Wild, + MultiLocation, OriginKind, ParentThen, + WeightLimit::Limited, + WildMultiAsset::All, + Xcm, + }, + VersionedXcm, }; use xcm_emulator::TestExt; @@ -46,13 +57,19 @@ use dip_consumer_runtime_template::{ Balances, BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, }; -use dip_provider_runtime_template::{DipProvider, Runtime as ProviderRuntime}; +use dip_provider_runtime_template::{ + ConsumerParachainTxBuilder, DipProvider, PolkadotXcm as ProviderXcmPallet, Runtime as ProviderRuntime, + UniversalLocation, +}; #[test] fn commit_identity() { Network::reset(); let did: DidIdentifier = para::provider::did_auth_key().public().into(); + let consumer_location: MultiLocation = ParentThen(X1(Parachain(para::consumer::PARA_ID))).into(); + let asset: MultiAsset = (Here, 1_000_000_000).into(); + let weight = Weight::from_ref_time(4_000); let provider_parachain_on_consumer_parachain_balance_before = ConsumerParachain::execute_with(|| { Balances::free_balance(para::consumer::provider_parachain_account_on_consumer()) }); @@ -69,9 +86,9 @@ fn commit_identity() { } .into(), did.clone(), - Box::new(ParentThen(X1(Parachain(para::consumer::PARA_ID))).into()), - Box::new((Here, 1_000_000_000).into()), - Weight::from_ref_time(4_000), + Box::new(consumer_location.into_versioned()), + Box::new(asset.into()), + weight, )); }); // 2. Verify that the commitment has made it to the DIP consumer. @@ -192,3 +209,70 @@ fn commit_identity() { assert_eq!(details, Some(1u128)); }); } + +#[test] +fn user_generated_commit_identity() { + Network::reset(); + + let did: DidIdentifier = para::provider::did_auth_key().public().into(); + let consumer_location: MultiLocation = ParentThen(X1(Parachain(para::consumer::PARA_ID))).into(); + let asset: MultiAsset = (Here, 1_000_000_000).into(); + let weight = Weight::from_ref_time(4_000); + let dest_tx = ConsumerParachainTxBuilder::build(consumer_location, IdentityDetailsAction::Deleted(did)) + .expect("Provider Tx builder should not fail to create the encoded `Transact` call."); + let message = ProviderParachain::execute_with(|| { + Xcm::<()>(vec![ + WithdrawAsset(asset.clone().into()), + BuyExecution { + fees: asset, + weight_limit: Limited(weight), + }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: weight, + call: dest_tx, + }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: MultiLocation { + parents: 1, + interior: Here + .into_location() + .reanchored(&consumer_location, UniversalLocation::get()) + .unwrap() + .pushed_with_interior(AccountId32 { + network: None, + id: para::provider::DISPATCHER_ACCOUNT.into(), + }) + .unwrap() + .interior, + }, + }, + ]) + }); + // 1. Send identity commitment from DIP provider to DIP consumer via a + // user-dispatched XCM call using the XCM pallet (no parachain origin). + ProviderParachain::execute_with(|| { + assert_ok!(ProviderXcmPallet::send( + RawOrigin::Signed(para::provider::DISPATCHER_ACCOUNT).into(), + Box::new(consumer_location.into()), + Box::new(VersionedXcm::from(message)) + )); + }); + // 2. Verify that the commitment has NOT made it to the DIP consumer and must + // have failed, since this was a user-generated XCM message on the provider + // chain using the XCM pallet. + ConsumerParachain::execute_with(|| { + // 2.1 Verify that there was an XCM error. + println!("{:?}", System::events()); + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XcmpQueue(XcmpEvent::Fail { + error: _, + message_hash: _, + weight: _ + }) + ))); + }); +} From d683a3f67cbc4e7ad1e99d39a4fa441a61fbf807 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 7 Jun 2023 09:13:04 +0200 Subject: [PATCH 12/13] Add ExpectOrigin instruction first --- crates/kilt-dip-support/src/xcm.rs | 9 ++++++++- dip-template/runtimes/xcm-tests/src/tests.rs | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index d2a17353e6..b3627d743d 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -22,7 +22,7 @@ use parity_scale_codec::Encode; use sp_core::Get; use sp_std::marker::PhantomData; use xcm::v3::{ - Instruction::{BuyExecution, DepositAsset, DescendOrigin, RefundSurplus, Transact, WithdrawAsset}, + Instruction::{BuyExecution, DepositAsset, DescendOrigin, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset}, InteriorMultiLocation, Junction::AccountId32, Junctions::{Here, X1}, @@ -69,6 +69,11 @@ where // TODO: Set an error handler and an appendix to refund any leftover funds to // the provider parachain sovereign account. let operation = [[ + ExpectOrigin(Some( + Here.into_location() + .reanchored(&destination, UniversalLocationProvider::get()) + .unwrap(), + )), DescendOrigin(X1(AccountId32 { network: None, id: source.clone().into(), @@ -133,8 +138,10 @@ pub mod barriers { iter.next(), iter.next(), iter.next(), + iter.next(), ) { ( + Some(ExpectOrigin(..)), Some(DescendOrigin(X1(AccountId32 { .. }))), Some(WithdrawAsset { .. }), Some(BuyExecution { .. }), diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 1e8a231166..c44e45dd33 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -38,7 +38,7 @@ use sp_core::Pair; use sp_runtime::traits::Zero; use xcm::{ v3::{ - Instruction::{BuyExecution, DepositAsset, RefundSurplus, Transact, WithdrawAsset}, + Instruction::{BuyExecution, DepositAsset, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset}, Junction::{AccountId32, Parachain}, Junctions::{Here, X1}, MultiAsset, @@ -218,10 +218,15 @@ fn user_generated_commit_identity() { let consumer_location: MultiLocation = ParentThen(X1(Parachain(para::consumer::PARA_ID))).into(); let asset: MultiAsset = (Here, 1_000_000_000).into(); let weight = Weight::from_ref_time(4_000); - let dest_tx = ConsumerParachainTxBuilder::build(consumer_location, IdentityDetailsAction::Deleted(did)) + let dest_tx = ConsumerParachainTxBuilder::build(consumer_location, IdentityDetailsAction::Deleted(did.clone())) .expect("Provider Tx builder should not fail to create the encoded `Transact` call."); let message = ProviderParachain::execute_with(|| { Xcm::<()>(vec![ + ExpectOrigin(Some( + Here.into_location() + .reanchored(&consumer_location, UniversalLocation::get()) + .unwrap(), + )), WithdrawAsset(asset.clone().into()), BuyExecution { fees: asset, @@ -274,5 +279,7 @@ fn user_generated_commit_identity() { weight: _ }) ))); + // 2.2 Verify there is no storage entry in the consumer pallet. + assert!(DipConsumer::identity_proofs(&did).is_none()); }); } From 8ba834e9844ae8be760bea2dddae5ef49b24161a Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 7 Jun 2023 11:14:15 +0200 Subject: [PATCH 13/13] Add comments for instruction matcher --- crates/kilt-dip-support/src/xcm.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs index b3627d743d..8a84459242 100644 --- a/crates/kilt-dip-support/src/xcm.rs +++ b/crates/kilt-dip-support/src/xcm.rs @@ -111,7 +111,7 @@ where .concat(); // TODO: Restructure the trait to be able to inject the [Instruction] provider, // and unit test that. - debug_assert!(barriers::instruction_matcher(&operation).is_ok()); + debug_assert!(barriers::check_expected_dip_instruction_order(&operation).is_ok()); let op = Xcm(operation); Router::validate(&mut Some(destination), &mut Some(op)) } @@ -128,7 +128,11 @@ pub mod barriers { use xcm::v3::{Instruction, Junction::Parachain, ParentThen}; use xcm_executor::traits::ShouldExecute; - pub(crate) fn instruction_matcher(instructions: &[Instruction]) -> Result<(), ()> { + // Must match the order of instructions as produced by the provider's + // implementation of the `IdentityProofDispatcher` trait. + pub(crate) fn check_expected_dip_instruction_order( + instructions: &[Instruction], + ) -> Result<(), ()> { let mut iter = instructions.iter(); match ( iter.next(), @@ -141,16 +145,27 @@ pub mod barriers { iter.next(), ) { ( + // A first instruction different than `DescendOrigin` is needed to distinguish between user-triggered + // and parachain-triggered XCM messages, since also the XCM pallet always preprends user-created XCM + // messages with a `DescendOrigin` instruction. Some(ExpectOrigin(..)), + // Go down to user level to charge them for the XCM fees. Some(DescendOrigin(X1(AccountId32 { .. }))), + // Expect the user to first withdraw an asset to pay for the fees. Some(WithdrawAsset { .. }), + // Buy execution time. Some(BuyExecution { .. }), + // Although this is irrelevant since `origin_kind` can also be specified by a user, we use + // `OriginKind::Native` here to make clear this is a parachain-dispatched XCM message. Some(Transact { origin_kind: OriginKind::Native, .. }), + // Any unused weight is refunded. Some(RefundSurplus), + // Any unused assets are refunded back into the user's account. Some(DepositAsset { .. }), + // No more instructions are allowed. None, ) => Ok(()), _ => Err(()), @@ -183,7 +198,7 @@ pub mod barriers { *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), () ); - instruction_matcher(instructions) + check_expected_dip_instruction_order(instructions) } }