diff --git a/Cargo.lock b/Cargo.lock
index d033efc860..759e8c19fd 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",
@@ -2502,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",
@@ -2522,6 +2525,7 @@ dependencies = [
"sp-runtime",
"sp-std",
"xcm",
+ "xcm-builder",
"xcm-emulator",
"xcm-executor",
]
@@ -4221,6 +4225,7 @@ name = "kilt-dip-support"
version = "1.11.0-dev"
dependencies = [
"did",
+ "dip-support",
"frame-support",
"frame-system",
"pallet-dip-consumer",
@@ -4231,6 +4236,8 @@ dependencies = [
"sp-runtime",
"sp-std",
"sp-trie",
+ "xcm",
+ "xcm-executor",
]
[[package]]
@@ -6281,6 +6288,7 @@ dependencies = [
name = "pallet-dip-provider"
version = "1.11.0-dev"
dependencies = [
+ "did",
"dip-support",
"frame-support",
"frame-system",
diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml
index e06e17664e..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
@@ -28,10 +29,15 @@ sp-core.workspace = true
sp-trie.workspace = true
sp-std.workspace = true
+# Polkadot dependencies
+xcm.workspace = true
+xcm-executor.workspace = true
+
[features]
default = ["std"]
std = [
"did/std",
+ "dip-support/std",
"pallet-dip-consumer/std",
"pallet-dip-provider/std",
"parity-scale-codec/std",
@@ -41,6 +47,8 @@ std = [
"sp-runtime/std",
"sp-core/std",
"sp-trie/std",
- "sp-std/std"
+ "sp-std/std",
+ "xcm-executor/std",
+ "xcm/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..8a84459242
--- /dev/null
+++ b/crates/kilt-dip-support/src/xcm.rs
@@ -0,0 +1,289 @@
+// 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 dip_support::IdentityDetailsAction;
+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::{BuyExecution, DepositAsset, DescendOrigin, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset},
+ InteriorMultiLocation,
+ Junction::AccountId32,
+ Junctions::{Here, X1},
+ MultiAsset,
+ MultiAssetFilter::Wild,
+ MultiAssets, MultiLocation, OriginKind, SendError, SendXcm, Weight,
+ WeightLimit::Limited,
+ WildMultiAsset::All,
+ Xcm,
+};
+
+// 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 XcmRouterIdentityDispatcher(
+ PhantomData<(Router, UniversalLocationProvider)>,
+);
+
+impl
+ IdentityProofDispatcher
+ for XcmRouterIdentityDispatcher
+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 = [[
+ ExpectOrigin(Some(
+ Here.into_location()
+ .reanchored(&destination, UniversalLocationProvider::get())
+ .unwrap(),
+ )),
+ 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();
+ // TODO: Restructure the trait to be able to inject the [Instruction] provider,
+ // and unit test that.
+ debug_assert!(barriers::check_expected_dip_instruction_order(&operation).is_ok());
+ 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 mod barriers {
+ use super::*;
+
+ use frame_support::ensure;
+ use xcm::v3::{Instruction, Junction::Parachain, ParentThen};
+ use xcm_executor::traits::ShouldExecute;
+
+ // 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(),
+ iter.next(),
+ iter.next(),
+ iter.next(),
+ iter.next(),
+ iter.next(),
+ iter.next(),
+ 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(()),
+ }
+ }
+
+ // 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<(), ()> {
+ #[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(),
+ ()
+ );
+ check_expected_dip_instruction_order(instructions)
+ }
+ }
+
+ // 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 bb7775be4f..83d2918888 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::{barriers::OkOrElseCheckForParachainProvider, origins::AccountIdJunctionAsParachain};
use pallet_xcm::TestWeightInfo;
-use polkadot_parachain::primitives::Sibling;
-use xcm::latest::prelude::*;
+use xcm::v3::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 = OkOrElseCheckForParachainProvider, 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/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 b8559c997c..59b6aedade 100644
--- a/dip-template/runtimes/dip-provider/src/dip.rs
+++ b/dip-template/runtimes/dip-provider/src/dip.rs
@@ -16,16 +16,15 @@
// If you feel like getting in touch with us, you can do so at info@botlabs.org
-use did::EnsureDidOrigin;
+use did::{DidRawOrigin, EnsureDidOrigin};
use dip_support::IdentityDetailsAction;
-use frame_support::traits::EitherOfDiverse;
-use frame_system::EnsureRoot;
-use pallet_dip_provider::traits::{TxBuilder, 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};
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 +55,10 @@ impl TxBuilder for ConsumerParachainTxBuilder {
}
impl pallet_dip_provider::Config for Runtime {
- type CommitOrigin = EitherOfDiverse, EnsureDidOrigin>;
+ type CommitOriginCheck = EnsureDidOrigin;
+ type CommitOrigin = DidRawOrigin;
type Identifier = DidIdentifier;
- type IdentityProofDispatcher = XcmRouterDispatcher;
+ type IdentityProofDispatcher = XcmRouterIdentityDispatcher;
type IdentityProofGenerator = DidMerkleRootGenerator;
type IdentityProvider = LinkedDidInfoProviderOf;
type ProofOutput = Hash;
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/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml
index ae3b735c77..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"]}
@@ -35,6 +37,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..a24076fe04 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::v3::{
+ 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::v3::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..c44e45dd33 100644
--- a/dip-template/runtimes/xcm-tests/src/tests.rs
+++ b/dip-template/runtimes/xcm-tests/src/tests.rs
@@ -18,7 +18,8 @@
use super::*;
-use did::{Did, DidSignature};
+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,34 +36,59 @@ use runtime_common::dip::{
};
use sp_core::Pair;
use sp_runtime::traits::Zero;
-use xcm::latest::{
- Junction::Parachain,
- Junctions::{Here, X1},
- ParentThen,
+use xcm::{
+ v3::{
+ Instruction::{BuyExecution, DepositAsset, ExpectOrigin, 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;
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::{
+ ConsumerParachainTxBuilder, DipProvider, PolkadotXcm as ProviderXcmPallet, Runtime as ProviderRuntime,
+ UniversalLocation,
};
-use dip_provider_runtime_template::{DipProvider, Runtime as ProviderRuntime};
#[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())
+ });
+ 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(|| {
assert_ok!(DipProvider::commit_identity(
- RawOrigin::Root.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()),
- 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.
@@ -77,6 +104,19 @@ 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(para::consumer::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(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
let did_details = ProviderParachain::execute_with(|| {
@@ -169,3 +209,77 @@ 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.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,
+ 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: _
+ })
+ )));
+ // 2.2 Verify there is no storage entry in the consumer pallet.
+ assert!(DipConsumer::identity_proofs(&did).is_none());
+ });
+}
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 cf2983fa2d..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>;
@@ -44,14 +44,15 @@ pub mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {
- type CommitOrigin: EnsureOrigin;
+ type CommitOriginCheck: EnsureOrigin;
+ type CommitOrigin: SubmitterInfo;
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>;
@@ -90,8 +91,7 @@ pub mod pallet {
asset: Box,
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.submitter())?;
let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?;
let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) {
@@ -107,12 +107,17 @@ 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.
+ // 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 12784472e4..f5a8836d1f 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};
@@ -53,15 +54,14 @@ pub mod identity_dispatch {
use super::*;
use frame_support::weights::Weight;
- use parity_scale_codec::Encode;
- use sp_std::{marker::PhantomData, vec};
- 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 +73,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,
@@ -92,59 +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, Identifier, ProofOutput, Details)>,
- );
-
- impl IdentityProofDispatcher
- for XcmRouterDispatcher
- where
- Router: SendXcm,
- Identifier: Encode,
- ProofOutput: Encode,
- {
- type PreDispatchOutput = Router::Ticket;
- type Error = SendError;
-
- fn pre_dispatch>(
- action: IdentityDetailsAction,
- 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 = [vec![
- WithdrawAsset(asset.clone().into()),
- BuyExecution {
- fees: asset,
- // TODO: Configurable weight limit?
- weight_limit: Unlimited,
- },
- Transact {
- origin_kind: OriginKind::Native,
- require_weight_at_most: weight,
- call: dest_tx,
- },
- ]]
- .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::*;
@@ -196,3 +144,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()
+ }
+}
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,