From 665bd6c2dc9770be2c905de5bd57401ffd1d9c85 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 30 Oct 2021 23:59:39 +0200 Subject: [PATCH 01/57] XCM bridge infrastructure --- runtime/kusama/src/lib.rs | 8 +- runtime/rococo/src/lib.rs | 3 +- runtime/test-runtime/src/lib.rs | 4 +- runtime/test-runtime/src/xcm_config.rs | 4 + runtime/westend/src/lib.rs | 3 +- .../src/fungible/mock.rs | 1 + xcm/pallet-xcm-benchmarks/src/mock.rs | 2 +- xcm/pallet-xcm/src/mock.rs | 7 +- xcm/pallet-xcm/src/tests.rs | 14 +- xcm/procedural/src/lib.rs | 8 + xcm/procedural/src/v1/multilocation.rs | 30 ++ xcm/src/lib.rs | 93 ++++- xcm/src/v0/junction.rs | 15 + xcm/src/v1/junction.rs | 53 ++- xcm/src/v1/multiasset.rs | 61 +++- xcm/src/v1/multilocation.rs | 25 +- xcm/src/v2/mod.rs | 78 ++-- xcm/src/v3/mod.rs | 103 ++++-- xcm/src/v3/multiasset.rs | 335 ++++++++++++++++-- xcm/src/v3/traits.rs | 3 + xcm/xcm-builder/src/location_conversion.rs | 53 ++- xcm/xcm-builder/src/mock.rs | 16 +- xcm/xcm-builder/src/origin_conversion.rs | 10 +- xcm/xcm-builder/src/tests.rs | 41 ++- xcm/xcm-builder/tests/mock/mod.rs | 3 +- xcm/xcm-executor/src/config.rs | 8 +- xcm/xcm-executor/src/lib.rs | 19 +- xcm/xcm-executor/src/traits/conversion.rs | 26 +- xcm/xcm-simulator/example/src/lib.rs | 2 +- xcm/xcm-simulator/example/src/parachain.rs | 3 +- xcm/xcm-simulator/example/src/relay_chain.rs | 3 +- xcm/xcm-simulator/fuzzer/src/parachain.rs | 3 +- xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 3 +- 33 files changed, 806 insertions(+), 234 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index ce504a0b5ce1..9afafe1fed49 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1323,9 +1323,10 @@ parameter_types! { pub const KsmLocation: MultiLocation = Here.into(); /// The Kusama network ID. This is named. pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - /// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since - /// Kusama is a top-level relay-chain, there is no ancestry. - pub const Ancestry: MultiLocation = Here.into(); + /// Our XCM location ancestry - i.e. our location within the Consensus Universe. + /// + /// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID. + pub const Ancestry: InteriorMultiLocation = X1(GlobalConsensus(NetworkId::Kusama)); /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -1431,6 +1432,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } parameter_types! { diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 2f84fbc52d0a..632d03c1bd48 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -637,7 +637,7 @@ impl parachains_paras::Config for Runtime { parameter_types! { pub const RocLocation: MultiLocation = Here.into(); pub const RococoNetwork: NetworkId = NetworkId::Polkadot; - pub const Ancestry: MultiLocation = Here.into(); + pub const Ancestry: InteriorMultiLocation = GeneralKey(b"rococo".into()); pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -736,6 +736,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } parameter_types! { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 2980e45e529e..2583340c2c7f 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -606,7 +606,7 @@ pub mod pallet_test_notifier { .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) .map_err(|_| Error::::BadAccountFormat)?; let qid = pallet_xcm::Pallet::::new_query( - Junction::AccountId32 { network: Any, id }.into(), + Junction::AccountId32 { network: None, id }.into(), 100u32.into(), ); Self::deposit_event(Event::::QueryPrepared(qid)); @@ -622,7 +622,7 @@ pub mod pallet_test_notifier { let call = Call::::notification_received { query_id: 0, response: Default::default() }; let qid = pallet_xcm::Pallet::::new_notify_query( - Junction::AccountId32 { network: Any, id }.into(), + Junction::AccountId32 { network: None, id }.into(), ::Call::from(call), 100u32.into(), ); diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 77dded583479..6ce7498ee6a6 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -75,6 +75,9 @@ impl InvertLocation for InvertNothing { fn ancestry() -> MultiLocation { Here.into() } + fn universal_location() -> InteriorMultiLocation { + Ok(Here.into()) + } } pub struct XcmConfig; @@ -95,4 +98,5 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = super::Xcm; type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index f9032b9f50a3..a64fd9f7ac73 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -981,7 +981,7 @@ impl auctions::Config for Runtime { parameter_types! { pub const WndLocation: MultiLocation = Here.into(); - pub const Ancestry: MultiLocation = Here.into(); + pub const Ancestry: InteriorMultiLocation = GeneralKey(b"westend".into()); pub WestendNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec()); pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -1061,6 +1061,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 6647ff967999..5b7c2362b3c7 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -145,6 +145,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } impl crate::Config for Test { diff --git a/xcm/pallet-xcm-benchmarks/src/mock.rs b/xcm/pallet-xcm-benchmarks/src/mock.rs index d59cf3387268..ab0664984be1 100644 --- a/xcm/pallet-xcm-benchmarks/src/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/mock.rs @@ -51,7 +51,7 @@ impl xcm_executor::traits::Convert for AccountIdConverter { } parameter_types! { - pub Ancestry: MultiLocation = Junction::Parachain(101).into(); + pub Ancestry: InteriorMultiLocation = Junction::Parachain(101).into(); pub UnitWeightCost: Weight = 10; pub WeightPrice: (AssetId, u128) = (Concrete(Here.into()), 1_000_000); } diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index af9ef8f98548..0f64ea44cda2 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -80,7 +80,7 @@ pub mod pallet_test_notifier { .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) .map_err(|_| Error::::BadAccountFormat)?; let qid = crate::Pallet::::new_query( - Junction::AccountId32 { network: Any, id }.into(), + Junction::AccountId32 { network: None, id }.into(), 100u32.into(), ); Self::deposit_event(Event::::QueryPrepared(qid)); @@ -96,7 +96,7 @@ pub mod pallet_test_notifier { let call = Call::::notification_received { query_id: 0, response: Default::default() }; let qid = crate::Pallet::::new_notify_query( - Junction::AccountId32 { network: Any, id }.into(), + Junction::AccountId32 { network: None, id }.into(), ::Call::from(call), 100u32.into(), ); @@ -218,7 +218,7 @@ impl pallet_balances::Config for Test { parameter_types! { pub const RelayLocation: MultiLocation = Here.into(); pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here.into(); pub UnitWeightCost: Weight = 1_000; } @@ -268,6 +268,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 7b9b6ce864c6..f902d53f899c 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -39,7 +39,7 @@ const SEND_AMOUNT: u128 = 10; fn report_outcome_notify_works() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; - let sender = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + let sender = AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); let mut message = Xcm(vec![TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone(), @@ -99,7 +99,7 @@ fn report_outcome_notify_works() { fn report_outcome_works() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; - let sender = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + let sender = AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); let mut message = Xcm(vec![TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone(), @@ -153,7 +153,7 @@ fn send_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let sender: MultiLocation = - AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); let message = Xcm(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), ClearOrigin, @@ -190,7 +190,7 @@ fn send_fails_when_xcm_router_blocks() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let sender: MultiLocation = - Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + Junction::AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); let message = Xcm(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), buy_execution((Parent, SEND_AMOUNT)), @@ -218,7 +218,7 @@ fn teleport_assets_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = 2 * BaseXcmWeight::get(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); - let dest: MultiLocation = AccountId32 { network: Any, id: BOB.into() }.into(); + let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::teleport_assets( Origin::signed(ALICE), Box::new(RelayLocation::get().into()), @@ -259,7 +259,7 @@ fn limmited_teleport_assets_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = 2 * BaseXcmWeight::get(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); - let dest: MultiLocation = AccountId32 { network: Any, id: BOB.into() }.into(); + let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::limited_teleport_assets( Origin::signed(ALICE), Box::new(RelayLocation::get().into()), @@ -301,7 +301,7 @@ fn unlimmited_teleport_assets_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = 2 * BaseXcmWeight::get(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); - let dest: MultiLocation = AccountId32 { network: Any, id: BOB.into() }.into(); + let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::limited_teleport_assets( Origin::signed(ALICE), Box::new(RelayLocation::get().into()), diff --git a/xcm/procedural/src/lib.rs b/xcm/procedural/src/lib.rs index 8e43569b64d7..1ad801341f03 100644 --- a/xcm/procedural/src/lib.rs +++ b/xcm/procedural/src/lib.rs @@ -21,6 +21,7 @@ use proc_macro::TokenStream; mod v0; mod v1; mod weight_info; +mod v3; #[proc_macro] pub fn impl_conversion_functions_for_multilocation_v0(input: TokenStream) -> TokenStream { @@ -40,3 +41,10 @@ pub fn impl_conversion_functions_for_multilocation_v1(input: TokenStream) -> Tok pub fn derive_xcm_weight_info(item: TokenStream) -> TokenStream { weight_info::derive(item) } + +#[proc_macro] +pub fn impl_conversion_functions_for_multilocation_v3(input: TokenStream) -> TokenStream { + v3::multilocation::generate_conversion_functions(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/xcm/procedural/src/v1/multilocation.rs b/xcm/procedural/src/v1/multilocation.rs index eae3d677b075..2fe7dbee8c1f 100644 --- a/xcm/procedural/src/v1/multilocation.rs +++ b/xcm/procedural/src/v1/multilocation.rs @@ -26,10 +26,12 @@ pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result TokenStream { } } } + +fn generate_conversion_from_v3() -> TokenStream { + let match_variants = (0..8u8) + .map(|cur_num| { + let num_ancestors = cur_num + 1; + let variant = format_ident!("X{}", num_ancestors); + let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); + + quote! { + crate::v3::Junctions::#variant( #(#idents),* ) => + #variant( #( core::convert::TryInto::try_into(#idents)? ),* ), + } + }) + .collect::(); + + quote! { + impl core::convert::TryFrom for Junctions { + type Error = (); + fn try_from(mut new: crate::v3::Junctions) -> core::result::Result { + use Junctions::*; + Ok(match new { + crate::v3::Junctions::Here => Here, + #match_variants + }) + } + } + } +} diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index e7e1a98b3a61..6d757d6de9b1 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -78,13 +78,15 @@ pub trait IntoVersion: Sized { pub enum VersionedMultiLocation { V0(v0::MultiLocation), V1(v1::MultiLocation), + V3(v3::MultiLocation) } impl IntoVersion for VersionedMultiLocation { fn into_version(self, n: Version) -> Result { Ok(match n { 0 => Self::V0(self.try_into()?), - 1 | 2 | 3 => Self::V1(self.try_into()?), + 1 | 2 => Self::V1(self.try_into()?), + 3 => Self::V3(self.try_into()?), _ => return Err(()), }) } @@ -96,9 +98,15 @@ impl From for VersionedMultiLocation { } } -impl> From for VersionedMultiLocation { +impl From for VersionedMultiLocation { + fn from(x: v1::MultiLocation) -> Self { + VersionedMultiLocation::V1(x) + } +} + +impl> From for VersionedMultiLocation { fn from(x: T) -> Self { - VersionedMultiLocation::V1(x.into()) + VersionedMultiLocation::V3(x.into()) } } @@ -109,6 +117,7 @@ impl TryFrom for v0::MultiLocation { match x { V0(x) => Ok(x), V1(x) => x.try_into(), + V3(x) => V1(x.try_into()?).try_into(), } } } @@ -120,6 +129,19 @@ impl TryFrom for v1::MultiLocation { match x { V0(x) => x.try_into(), V1(x) => Ok(x), + V3(x) => x.try_into(), + } + } +} + +impl TryFrom for v3::MultiLocation { + type Error = (); + fn try_from(x: VersionedMultiLocation) -> Result { + use VersionedMultiLocation::*; + match x { + V0(x) => V1(x.try_into()?).try_into(), + V1(x) => x.try_into(), + V3(x) => Ok(x), } } } @@ -232,13 +254,15 @@ impl TryFrom for v3::Response { pub enum VersionedMultiAsset { V0(v0::MultiAsset), V1(v1::MultiAsset), + V3(v3::MultiAsset), } impl IntoVersion for VersionedMultiAsset { fn into_version(self, n: Version) -> Result { Ok(match n { 0 => Self::V0(self.try_into()?), - 1 | 2 | 3 => Self::V1(self.try_into()?), + 1 | 2 => Self::V1(self.try_into()?), + 3 => Self::V3(self.try_into()?), _ => return Err(()), }) } @@ -250,9 +274,15 @@ impl From for VersionedMultiAsset { } } -impl> From for VersionedMultiAsset { - fn from(x: T) -> Self { - VersionedMultiAsset::V1(x.into()) +impl From for VersionedMultiAsset { + fn from(x: v1::MultiAsset) -> Self { + VersionedMultiAsset::V1(x) + } +} + +impl From for VersionedMultiAsset { + fn from(x: v3::MultiAsset) -> Self { + VersionedMultiAsset::V3(x) } } @@ -263,6 +293,7 @@ impl TryFrom for v0::MultiAsset { match x { V0(x) => Ok(x), V1(x) => x.try_into(), + V3(x) => V1(x.try_into()?).try_into(), } } } @@ -274,6 +305,19 @@ impl TryFrom for v1::MultiAsset { match x { V0(x) => x.try_into(), V1(x) => Ok(x), + V3(x) => x.try_into(), + } + } +} + +impl TryFrom for v3::MultiAsset { + type Error = (); + fn try_from(x: VersionedMultiAsset) -> Result { + use VersionedMultiAsset::*; + match x { + V0(x) => V1(x.try_into()?).try_into(), + V1(x) => x.try_into(), + V3(x) => Ok(x), } } } @@ -286,13 +330,15 @@ impl TryFrom for v1::MultiAsset { pub enum VersionedMultiAssets { V0(Vec), V1(v1::MultiAssets), + V3(v3::MultiAssets), } impl IntoVersion for VersionedMultiAssets { fn into_version(self, n: Version) -> Result { Ok(match n { 0 => Self::V0(self.try_into()?), - 1 | 2 | 3 => Self::V1(self.try_into()?), + 1 | 2 => Self::V1(self.try_into()?), + 3 => Self::V3(self.try_into()?), _ => return Err(()), }) } @@ -304,9 +350,15 @@ impl From> for VersionedMultiAssets { } } -impl> From for VersionedMultiAssets { - fn from(x: T) -> Self { - VersionedMultiAssets::V1(x.into()) +impl From for VersionedMultiAssets { + fn from(x: v1::MultiAssets) -> Self { + VersionedMultiAssets::V1(x) + } +} + +impl From for VersionedMultiAssets { + fn from(x: v3::MultiAssets) -> Self { + VersionedMultiAssets::V3(x) } } @@ -317,6 +369,7 @@ impl TryFrom for Vec { match x { V0(x) => Ok(x), V1(x) => x.try_into(), + V3(x) => V1(x.try_into()?).try_into(), } } } @@ -328,6 +381,19 @@ impl TryFrom for v1::MultiAssets { match x { V0(x) => x.try_into(), V1(x) => Ok(x), + V3(x) => x.try_into(), + } + } +} + +impl TryFrom for v3::MultiAssets { + type Error = (); + fn try_from(x: VersionedMultiAssets) -> Result { + use VersionedMultiAssets::*; + match x { + V0(x) => V1(x.try_into()?).try_into(), + V1(x) => x.try_into(), + V3(x) => Ok(x), } } } @@ -525,3 +591,8 @@ pub mod opaque { pub trait GetWeight { fn weight(&self) -> latest::Weight; } + +#[test] +fn test_build() { + +} \ No newline at end of file diff --git a/xcm/src/v0/junction.rs b/xcm/src/v0/junction.rs index 0c559ca5a136..f8af909e7a45 100644 --- a/xcm/src/v0/junction.rs +++ b/xcm/src/v0/junction.rs @@ -16,9 +16,11 @@ //! Support data structures for `MultiLocation`, primarily the `Junction` datatype. +use core::convert::TryInto; use alloc::vec::Vec; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; +use crate::v3::NetworkId as NewNetworkId; /// A global identifier of an account-bearing consensus system. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] @@ -33,6 +35,19 @@ pub enum NetworkId { Kusama, } +impl TryInto for Option { + type Error = (); + fn try_into(self) -> Result { + use NewNetworkId::*; + Ok(match self { + None => NetworkId::Any, + Some(Named(name)) => NetworkId::Named(name), + Some(Polkadot) => NetworkId::Polkadot, + Some(Kusama) => NetworkId::Kusama, + }) + } +} + /// An identifier of a pluralistic body. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] pub enum BodyId { diff --git a/xcm/src/v1/junction.rs b/xcm/src/v1/junction.rs index c4835d60c5b1..62b5a125f5a2 100644 --- a/xcm/src/v1/junction.rs +++ b/xcm/src/v1/junction.rs @@ -17,9 +17,10 @@ //! Support data structures for `MultiLocation`, primarily the `Junction` datatype. use super::{BodyId, BodyPart, Junctions, MultiLocation, NetworkId}; -use crate::v0::Junction as Junction0; +use crate::v0::Junction as OldJunction; +use crate::v3::Junction as NewJunction; use alloc::vec::Vec; -use core::convert::TryFrom; +use core::convert::{TryFrom, TryInto}; use parity_scale_codec::{self, Decode, Encode}; use scale_info::TypeInfo; @@ -78,26 +79,50 @@ pub enum Junction { Plurality { id: BodyId, part: BodyPart }, } -impl TryFrom for Junction { +impl TryFrom for Junction { type Error = (); - fn try_from(value: Junction0) -> Result { + fn try_from(value: OldJunction) -> Result { + use OldJunction::*; match value { - Junction0::Parent => Err(()), - Junction0::Parachain(id) => Ok(Self::Parachain(id)), - Junction0::AccountId32 { network, id } => Ok(Self::AccountId32 { network, id }), - Junction0::AccountIndex64 { network, index } => + Parent => Err(()), + Parachain(id) => Ok(Self::Parachain(id)), + AccountId32 { network, id } => Ok(Self::AccountId32 { network, id }), + AccountIndex64 { network, index } => Ok(Self::AccountIndex64 { network, index }), - Junction0::AccountKey20 { network, key } => Ok(Self::AccountKey20 { network, key }), - Junction0::PalletInstance(index) => Ok(Self::PalletInstance(index)), - Junction0::GeneralIndex(id) => Ok(Self::GeneralIndex(id)), - Junction0::GeneralKey(key) => Ok(Self::GeneralKey(key)), - Junction0::OnlyChild => Ok(Self::OnlyChild), - Junction0::Plurality { id, part } => Ok(Self::Plurality { id: id.into(), part }), + AccountKey20 { network, key } => Ok(Self::AccountKey20 { network, key }), + PalletInstance(index) => Ok(Self::PalletInstance(index)), + GeneralIndex(id) => Ok(Self::GeneralIndex(id)), + GeneralKey(key) => Ok(Self::GeneralKey(key)), + OnlyChild => Ok(Self::OnlyChild), + Plurality { id, part } => Ok(Self::Plurality { id: id.into(), part }), } } } +impl TryFrom for Junction { + type Error = (); + + fn try_from(value: NewJunction) -> Result { + use NewJunction::*; + Ok(match value { + Parachain(id) => Self::Parachain(id), + AccountId32 { network, id } => + Self::AccountId32 { network: network.try_into()?, id }, + AccountIndex64 { network, index } => + Self::AccountIndex64 { network: network.try_into()?, index }, + AccountKey20 { network, key } => + Self::AccountKey20 { network: network.try_into()?, key }, + PalletInstance(index) => Self::PalletInstance(index), + GeneralIndex(id) => Self::GeneralIndex(id), + GeneralKey(key) => Self::GeneralKey(key), + OnlyChild => Self::OnlyChild, + Plurality { id, part } => Self::Plurality { id: id.into(), part }, + _ => return Err(()), + }) + } +} + impl Junction { /// Convert `self` into a `MultiLocation` containing 0 parents. /// diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index f4d0f1bf04be..def92a0161ee 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -24,7 +24,10 @@ //! account. use super::MultiLocation; -use crate::v3::{MultiAssetFilter as NewMultiAssetFilter, WildMultiAsset as NewWildMultiAsset}; +use crate::v3::{ + AssetId as NewAssetId, MultiAsset as NewMultiAsset, MultiAssets as NewMultiAssets, + MultiAssetFilter as NewMultiAssetFilter, WildMultiAsset as NewWildMultiAsset +}; use alloc::{vec, vec::Vec}; use core::{ cmp::Ordering, @@ -115,6 +118,17 @@ impl From> for AssetId { } } +impl TryFrom for AssetId { + type Error = (); + fn try_from(old: NewAssetId) -> Result { + use NewAssetId::*; + Ok(match old { + Concrete(l) => Self::Concrete(l.try_into()?), + Abstract(v) => Self::Abstract(v), + }) + } +} + impl AssetId { /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> { @@ -287,6 +301,13 @@ impl TryFrom> for MultiAsset { } } +impl TryFrom for MultiAsset { + type Error = (); + fn try_from(new: NewMultiAsset) -> Result { + Ok(Self { id: new.id.try_into()?, fun: new.fun }) + } +} + /// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo)] pub struct MultiAssets(Vec); @@ -310,6 +331,18 @@ impl TryFrom> for MultiAssets { } } +impl TryFrom for MultiAssets { + type Error = (); + fn try_from(new: NewMultiAssets) -> Result { + let v = new + .drain() + .into_iter() + .map(MultiAsset::try_from) + .collect::, ()>>()?; + Ok(MultiAssets(v)) + } +} + impl From> for MultiAssets { fn from(mut assets: Vec) -> Self { let mut res = Vec::with_capacity(assets.len()); @@ -451,6 +484,7 @@ impl MultiAssets { self.0.get(index) } } + /// Classification of whether an asset is fungible or not. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] pub enum WildFungibility { @@ -597,22 +631,25 @@ impl TryFrom> for MultiAssetFilter { } } -impl From for WildMultiAsset { - fn from(old: NewWildMultiAsset) -> Self { +impl TryFrom for WildMultiAsset { + type Error = (); + fn try_from(new: NewWildMultiAsset) -> Result { use NewWildMultiAsset::*; - match old { - AllOf { id, fun } | AllOfCounted { id, fun, .. } => Self::AllOf { id, fun }, + Ok(match new { + AllOf { id, fun } | AllOfCounted { id, fun, .. } => + Self::AllOf { id: id.try_into()?, fun }, All | AllCounted(_) => Self::All, - } + }) } } -impl From for MultiAssetFilter { - fn from(old: NewMultiAssetFilter) -> Self { +impl TryFrom for MultiAssetFilter { + type Error = (); + fn try_from(old: NewMultiAssetFilter) -> Result { use NewMultiAssetFilter::*; - match old { - Definite(x) => Self::Definite(x), - Wild(x) => Self::Wild(x.into()), - } + Ok(match old { + Definite(x) => Self::Definite(x.try_into()?), + Wild(x) => Self::Wild(x.try_into()?), + }) } } diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index 12b507329215..8ce774ff6e55 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -17,7 +17,8 @@ //! Cross-Consensus Message format data structures. use super::Junction; -use core::{convert::TryFrom, mem, result}; +use crate::v3::MultiLocation as NewMultiLocation; +use core::{convert::{TryFrom, TryInto}, mem, result}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -379,6 +380,16 @@ impl MultiLocation { } } +impl TryFrom for MultiLocation { + type Error = (); + fn try_from(x: NewMultiLocation) -> result::Result { + Ok(MultiLocation { + parents: x.parents, + interior: x.interior.try_into()?, + }) + } +} + /// A unit struct which can be converted into a `MultiLocation` of `parents` value 1. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Parent; @@ -390,7 +401,7 @@ impl From for MultiLocation { /// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner interior. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct ParentThen(Junctions); +pub struct ParentThen(pub Junctions); impl From for MultiLocation { fn from(ParentThen(interior): ParentThen) -> Self { MultiLocation { parents: 1, interior } @@ -399,7 +410,7 @@ impl From for MultiLocation { /// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Ancestor(u8); +pub struct Ancestor(pub u8); impl From for MultiLocation { fn from(Ancestor(parents): Ancestor) -> Self { MultiLocation { parents, interior: Junctions::Here } @@ -408,10 +419,10 @@ impl From for MultiLocation { /// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the inner interior. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct AncestorThen(u8, Junctions); -impl From for MultiLocation { - fn from(AncestorThen(parents, interior): AncestorThen) -> Self { - MultiLocation { parents, interior } +pub struct AncestorThen(pub u8, pub Interior); +impl> From> for MultiLocation { + fn from(AncestorThen(parents, interior): AncestorThen) -> Self { + MultiLocation { parents, interior: interior.into() } } } diff --git a/xcm/src/v2/mod.rs b/xcm/src/v2/mod.rs index 22b3f81af697..5544d591d787 100644 --- a/xcm/src/v2/mod.rs +++ b/xcm/src/v2/mod.rs @@ -801,16 +801,16 @@ impl TryFrom for Response { impl TryFrom for Response { type Error = (); fn try_from(response: NewResponse) -> result::Result { - match response { - NewResponse::Assets(assets) => Ok(Self::Assets(assets)), - NewResponse::Version(version) => Ok(Self::Version(version)), - NewResponse::ExecutionResult(error) => Ok(Self::ExecutionResult(match error { + Ok(match response { + NewResponse::Assets(assets) => Self::Assets(assets.try_into()?), + NewResponse::Version(version) => Self::Version(version), + NewResponse::ExecutionResult(error) => Self::ExecutionResult(match error { Some((i, e)) => Some((i, e.try_into()?)), None => None, - })), - NewResponse::Null => Ok(Self::Null), - _ => Err(()), - } + }), + NewResponse::Null => Self::Null, + _ => return Err(()), + }) } } @@ -929,14 +929,15 @@ impl TryFrom> for Instruction { fn try_from(instruction: NewInstruction) -> result::Result { use NewInstruction::*; Ok(match instruction { - WithdrawAsset(assets) => Self::WithdrawAsset(assets), - ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets), - ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets), + WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), + ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), + ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), QueryResponse { query_id, response, max_weight } => Self::QueryResponse { query_id, response: response.try_into()?, max_weight }, - TransferAsset { assets, beneficiary } => Self::TransferAsset { assets, beneficiary }, + TransferAsset { assets, beneficiary } => + Self::TransferAsset { assets: assets.try_into()?, beneficiary: beneficiary.try_into()? }, TransferReserveAsset { assets, dest, xcm } => - Self::TransferReserveAsset { assets, dest, xcm: xcm.try_into()? }, + Self::TransferReserveAsset { assets: assets.try_into()?, dest: dest.try_into()?, xcm: xcm.try_into()? }, HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, @@ -949,47 +950,62 @@ impl TryFrom> for Instruction { }, ReportError(response_info) => Self::ReportError { query_id: response_info.query_id, - dest: response_info.destination, + dest: response_info.destination.try_into()?, max_response_weight: response_info.max_weight, }, DepositAsset { assets, beneficiary } => { let max_assets = assets.count().ok_or(())?; - Self::DepositAsset { assets: assets.into(), max_assets, beneficiary } + let beneficiary = beneficiary.try_into()?; + let assets = assets.try_into()?; + Self::DepositAsset { assets, max_assets, beneficiary } }, DepositReserveAsset { assets, dest, xcm } => { let max_assets = assets.count().ok_or(())?; - Self::DepositReserveAsset { - assets: assets.into(), - max_assets, - dest, - xcm: xcm.try_into()?, - } + let dest = dest.try_into()?; + let xcm = xcm.try_into()?; + let assets = assets.try_into()?; + Self::DepositReserveAsset { assets, max_assets, dest, xcm } + }, + ExchangeAsset { give, receive } => { + let give = give.try_into()?; + let receive = receive.try_into()?; + Self::ExchangeAsset { give, receive } }, - ExchangeAsset { give, receive } => Self::ExchangeAsset { give: give.into(), receive }, InitiateReserveWithdraw { assets, reserve, xcm } => { // No `max_assets` here, so if there's a connt, then we cannot translate. - let assets = assets.try_into().map_err(|_| ())?; - Self::InitiateReserveWithdraw { assets, reserve, xcm: xcm.try_into()? } + let assets = assets.try_into()?; + let reserve = reserve.try_into()?; + let xcm = xcm.try_into()?; + Self::InitiateReserveWithdraw { assets, reserve, xcm } }, InitiateTeleport { assets, dest, xcm } => { // No `max_assets` here, so if there's a connt, then we cannot translate. - let assets = assets.try_into().map_err(|_| ())?; - Self::InitiateTeleport { assets, dest, xcm: xcm.try_into()? } + let assets = assets.try_into()?; + let dest = dest.try_into()?; + let xcm = xcm.try_into()?; + Self::InitiateTeleport { assets, dest, xcm } }, ReportHolding { response_info, assets } => Self::QueryHolding { query_id: response_info.query_id, - dest: response_info.destination, - assets: assets.try_into().map_err(|_| ())?, + dest: response_info.destination.try_into()?, + assets: assets.try_into()?, max_response_weight: response_info.max_weight, }, - BuyExecution { fees, weight_limit } => Self::BuyExecution { fees, weight_limit }, + BuyExecution { fees, weight_limit } => { + let fees = fees.try_into()?; + Self::BuyExecution { fees, weight_limit } + }, ClearOrigin => Self::ClearOrigin, - DescendOrigin(who) => Self::DescendOrigin(who), + DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), RefundSurplus => Self::RefundSurplus, SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?), SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?), ClearError => Self::ClearError, - ClaimAsset { assets, ticket } => Self::ClaimAsset { assets, ticket }, + ClaimAsset { assets, ticket } => { + let assets = assets.try_into()?; + let ticket = ticket.try_into()?; + Self::ClaimAsset { assets, ticket } + }, Trap(code) => Self::Trap(code), SubscribeVersion { query_id, max_response_weight } => Self::SubscribeVersion { query_id, max_response_weight }, diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 29429a6ceb03..ddba5da53471 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -30,6 +30,8 @@ use scale_info::TypeInfo; mod multiasset; mod traits; +mod junction; +mod multilocation; pub use multiasset::{ AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, @@ -38,11 +40,12 @@ pub use multiasset::{ pub use traits::{ Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo, }; -// These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. -pub use super::v2::{ - Ancestor, AncestorThen, BodyId, BodyPart, InteriorMultiLocation, Junction, Junctions, - MultiLocation, NetworkId, OriginKind, Parent, ParentThen, WeightLimit, +pub use junction::{Junction, NetworkId}; +pub use multilocation::{ + Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen, }; +// These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. +pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; /// This module's XCM version. pub const VERSION: super::Version = 3; @@ -126,12 +129,13 @@ pub mod prelude { InteriorMultiLocation, Junction::{self, *}, Junctions::{self, *}, + MaybeErrorCode, MultiAsset, MultiAssetFilter::{self, *}, MultiAssets, MultiLocation, NetworkId::{self, *}, - OriginKind, Outcome, Parent, ParentThen, QueryId, QueryResponseInfo, Response, - Result as XcmResult, SendError, SendResult, SendXcm, + OriginKind, Outcome, PalletInfo, Parent, ParentThen, QueryId, QueryResponseInfo, + Response, Result as XcmResult, SendError, SendResult, SendXcm, WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, @@ -710,6 +714,18 @@ pub enum Instruction { /// /// Errors: *Infallible*. ClearTransactStatus, + + /// Set the Origin Register to be some child of the Universal Ancestor. + /// + /// Safety: Should only be usable if the Origin is trusted to represent the Universal Ancestor + /// child in general. In general, no Origin should be able to represent the Universal Ancester + /// child which is the root of the local consensus system since it would by extension + /// allow it to act as any location within the local consensus. + /// + /// Kind: *Instruction* + /// + /// Errors: *Fallible*. + UniversalOrigin(Junction), } impl Xcm { @@ -773,6 +789,7 @@ impl Instruction { ExpectPallet { index, name, module_name, crate_major, min_crate_minor }, ReportTransactStatus(repsonse_info) => ReportTransactStatus(repsonse_info), ClearTransactStatus => ClearTransactStatus, + UniversalOrigin(j) => UniversalOrigin(j), } } } @@ -826,6 +843,7 @@ impl> GetWeight for Instruction { ExpectPallet { index, .. } => W::expect_pallet(index), ReportTransactStatus(response_info) => W::report_transact_status(response_info), ClearTransactStatus => W::clear_transact_status(), + UniversalOrigin(j) => W::universal_origin(j), } } } @@ -845,7 +863,7 @@ impl TryFrom for Response { type Error = (); fn try_from(old_response: OldResponse) -> result::Result { match old_response { - OldResponse::Assets(assets) => Ok(Self::Assets(assets)), + OldResponse::Assets(assets) => Ok(Self::Assets(assets.try_into()?)), OldResponse::Version(version) => Ok(Self::Version(version)), OldResponse::ExecutionResult(error) => Ok(Self::ExecutionResult(match error { Some((i, e)) => Some((i, e.try_into()?)), @@ -870,14 +888,14 @@ impl TryFrom> for Instruction { fn try_from(old_instruction: OldInstruction) -> result::Result { use OldInstruction::*; Ok(match old_instruction { - WithdrawAsset(assets) => Self::WithdrawAsset(assets), - ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets), - ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets), + WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), + ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), + ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), QueryResponse { query_id, response, max_weight } => Self::QueryResponse { query_id, response: response.try_into()?, max_weight }, - TransferAsset { assets, beneficiary } => Self::TransferAsset { assets, beneficiary }, + TransferAsset { assets, beneficiary } => Self::TransferAsset { assets: assets.try_into()?, beneficiary: beneficiary.try_into()? }, TransferReserveAsset { assets, dest, xcm } => - Self::TransferReserveAsset { assets, dest, xcm: xcm.try_into()? }, + Self::TransferReserveAsset { assets: assets.try_into()?, dest: dest.try_into()?, xcm: xcm.try_into()? }, HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, @@ -890,42 +908,52 @@ impl TryFrom> for Instruction { }, ReportError { query_id, dest, max_response_weight } => { let response_info = QueryResponseInfo { - destination: dest, + destination: dest.try_into()?, query_id, max_weight: max_response_weight, }; Self::ReportError(response_info) }, DepositAsset { assets, max_assets, beneficiary } => - Self::DepositAsset { assets: (assets, max_assets).try_into()?, beneficiary }, + Self::DepositAsset { assets: (assets, max_assets).try_into()?, beneficiary: beneficiary.try_into()? }, DepositReserveAsset { assets, max_assets, dest, xcm } => { let assets = (assets, max_assets).try_into()?; - Self::DepositReserveAsset { assets, dest, xcm: xcm.try_into()? } + Self::DepositReserveAsset { assets, dest: dest.try_into()?, xcm: xcm.try_into()? } + }, + ExchangeAsset { give, receive } => { + let give = give.try_into()?; + let receive = receive.try_into()?; + Self::ExchangeAsset { give, receive } }, - ExchangeAsset { give, receive } => Self::ExchangeAsset { give: give.into(), receive }, InitiateReserveWithdraw { assets, reserve, xcm } => Self::InitiateReserveWithdraw { - assets: assets.into(), - reserve, + assets: assets.try_into()?, + reserve: reserve.try_into()?, xcm: xcm.try_into()?, }, InitiateTeleport { assets, dest, xcm } => - Self::InitiateTeleport { assets: assets.into(), dest, xcm: xcm.try_into()? }, + Self::InitiateTeleport { assets: assets.try_into()?, dest: dest.try_into()?, xcm: xcm.try_into()? }, QueryHolding { query_id, dest, assets, max_response_weight } => { let response_info = QueryResponseInfo { - destination: dest, + destination: dest.try_into()?, query_id, max_weight: max_response_weight, }; - Self::ReportHolding { response_info, assets: assets.into() } + Self::ReportHolding { response_info, assets: assets.try_into()? } + }, + BuyExecution { fees, weight_limit } => { + Self::BuyExecution { fees: fees.try_into()?, weight_limit } }, - BuyExecution { fees, weight_limit } => Self::BuyExecution { fees, weight_limit }, ClearOrigin => Self::ClearOrigin, - DescendOrigin(who) => Self::DescendOrigin(who), + DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), RefundSurplus => Self::RefundSurplus, SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?), SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?), ClearError => Self::ClearError, - ClaimAsset { assets, ticket } => Self::ClaimAsset { assets, ticket }, + ClaimAsset { assets, ticket } => { + let assets = assets.try_into()?; + let ticket = ticket.try_into()?; + Self::ClaimAsset { assets, ticket } + }, Trap(code) => Self::Trap(code), SubscribeVersion { query_id, max_response_weight } => Self::SubscribeVersion { query_id, max_response_weight }, @@ -937,15 +965,18 @@ impl TryFrom> for Instruction { #[cfg(test)] mod tests { use super::{prelude::*, *}; - use crate::v2::{MultiAssetFilter as OldMultiAssetFilter, WildMultiAsset as OldWildMultiAsset}; + use crate::v2::{ + MultiAssetFilter as OldMultiAssetFilter, WildMultiAsset as OldWildMultiAsset, + Junctions::Here as OldHere, + }; #[test] fn basic_roundtrip_works() { let xcm = Xcm::<()>(vec![TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() }]); let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset { - assets: (Here, 1).into(), - beneficiary: Here.into(), + assets: (OldHere, 1).into(), + beneficiary: OldHere.into(), }]); assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); @@ -960,12 +991,12 @@ mod tests { DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, ]); let old_xcm: OldXcm<()> = OldXcm::<()>(vec![ - OldInstruction::ReceiveTeleportedAsset((Here, 1).into()), + OldInstruction::ReceiveTeleportedAsset((OldHere, 1).into()), OldInstruction::ClearOrigin, OldInstruction::DepositAsset { assets: crate::v2::MultiAssetFilter::Wild(crate::v2::WildMultiAsset::All), max_assets: 1, - beneficiary: Here.into(), + beneficiary: OldHere.into(), }, ]); assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); @@ -982,13 +1013,13 @@ mod tests { DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, ]); let old_xcm = OldXcm::<()>(vec![ - OldInstruction::ReserveAssetDeposited((Here, 1).into()), + OldInstruction::ReserveAssetDeposited((OldHere, 1).into()), OldInstruction::ClearOrigin, - OldInstruction::BuyExecution { fees: (Here, 1).into(), weight_limit: Some(1).into() }, + OldInstruction::BuyExecution { fees: (OldHere, 1).into(), weight_limit: Some(1).into() }, OldInstruction::DepositAsset { assets: crate::v2::MultiAssetFilter::Wild(crate::v2::WildMultiAsset::All), max_assets: 1, - beneficiary: Here.into(), + beneficiary: OldHere.into(), }, ]); assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); @@ -1003,11 +1034,11 @@ mod tests { DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, ]); let old_xcm = OldXcm::<()>(vec![ - OldInstruction::WithdrawAsset((Here, 1).into()), + OldInstruction::WithdrawAsset((OldHere, 1).into()), OldInstruction::DepositAsset { assets: OldMultiAssetFilter::Wild(OldWildMultiAsset::All), max_assets: 1, - beneficiary: Here.into(), + beneficiary: OldHere.into(), }, ]); assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); @@ -1026,11 +1057,11 @@ mod tests { }, ]); let old_xcm = OldXcm::<()>(vec![ - OldInstruction::WithdrawAsset((Here, 1).into()), + OldInstruction::WithdrawAsset((OldHere, 1).into()), OldInstruction::DepositReserveAsset { assets: OldMultiAssetFilter::Wild(OldWildMultiAsset::All), max_assets: 1, - dest: Here.into(), + dest: OldHere.into(), xcm: OldXcm::<()>(vec![]), }, ]); diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index 702e6d29ece5..6e1061210fac 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -24,16 +24,298 @@ //! account. use super::MultiLocation; -use crate::v2::{MultiAssetFilter as OldMultiAssetFilter, WildMultiAsset as OldWildMultiAsset}; +use crate::v2::{ + AssetId as OldAssetId, MultiAsset as OldMultiAsset, MultiAssets as OldMultiAssets, + MultiAssetFilter as OldMultiAssetFilter, WildMultiAsset as OldWildMultiAsset, +}; use alloc::{vec, vec::Vec}; -use core::convert::TryFrom; -use parity_scale_codec::{Decode, Encode}; +use core::{ + cmp::Ordering, + convert::{TryFrom, TryInto}, +}; +use parity_scale_codec::{self as codec, Decode, Encode}; use scale_info::TypeInfo; -/// These are unchanged from XCM version 2 to version 3. -pub use crate::v2::{ - AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssets, WildFungibility, -}; +pub use crate::v2::{AssetInstance, Fungibility, WildFungibility}; + +/// Classification of an asset being concrete or abstract. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] +pub enum AssetId { + Concrete(MultiLocation), + Abstract(Vec), +} + +impl> From for AssetId { + fn from(x: T) -> Self { + Self::Concrete(x.into()) + } +} + +impl From> for AssetId { + fn from(x: Vec) -> Self { + Self::Abstract(x) + } +} + +impl TryFrom for AssetId { + type Error = (); + fn try_from(old: OldAssetId) -> Result { + use OldAssetId::*; + Ok(match old { + Concrete(l) => Self::Concrete(l.try_into()?), + Abstract(v) => Self::Abstract(v), + }) + } +} + +impl AssetId { + /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + if let AssetId::Concrete(ref mut l) = self { + l.prepend_with(prepend.clone()).map_err(|_| ())?; + } + Ok(()) + } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value. + pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset { + MultiAsset { fun, id: self } + } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset` + /// wildcard (`AllOf`) value. + pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset { + WildMultiAsset::AllOf { fun, id: self } + } +} + +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] +pub struct MultiAsset { + pub id: AssetId, + pub fun: Fungibility, +} + +impl PartialOrd for MultiAsset { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for MultiAsset { + fn cmp(&self, other: &Self) -> Ordering { + match (&self.fun, &other.fun) { + (Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less, + (Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater, + _ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)), + } + } +} + +impl, B: Into> From<(A, B)> for MultiAsset { + fn from((id, fun): (A, B)) -> MultiAsset { + MultiAsset { fun: fun.into(), id: id.into() } + } +} + +impl MultiAsset { + pub fn is_fungible(&self, maybe_id: Option) -> bool { + use Fungibility::*; + matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id) + } + + pub fn is_non_fungible(&self, maybe_id: Option) -> bool { + use Fungibility::*; + matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id) + } + + /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + self.id.reanchor(prepend) + } + + /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. + pub fn reanchored(mut self, prepend: &MultiLocation) -> Result { + self.reanchor(prepend)?; + Ok(self) + } + + /// Returns true if `self` is a super-set of the given `inner`. + pub fn contains(&self, inner: &MultiAsset) -> bool { + use Fungibility::*; + if self.id == inner.id { + match (&self.fun, &inner.fun) { + (Fungible(a), Fungible(i)) if a >= i => return true, + (NonFungible(a), NonFungible(i)) if a == i => return true, + _ => (), + } + } + false + } +} + +impl TryFrom for MultiAsset { + type Error = (); + fn try_from(old: OldMultiAsset) -> Result { + Ok(Self { id: old.id.try_into()?, fun: old.fun }) + } +} + +/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo)] +pub struct MultiAssets(Vec); + +impl Decode for MultiAssets { + fn decode(input: &mut I) -> Result { + Self::from_sorted_and_deduplicated(Vec::::decode(input)?) + .map_err(|()| "Out of order".into()) + } +} + +impl TryFrom for MultiAssets { + type Error = (); + fn try_from(old: OldMultiAssets) -> Result { + let v = old + .drain() + .into_iter() + .map(MultiAsset::try_from) + .collect::, ()>>()?; + Ok(MultiAssets(v)) + } +} + +impl From> for MultiAssets { + fn from(mut assets: Vec) -> Self { + let mut res = Vec::with_capacity(assets.len()); + if !assets.is_empty() { + assets.sort(); + let mut iter = assets.into_iter(); + if let Some(first) = iter.next() { + let last = iter.fold(first, |a, b| -> MultiAsset { + match (a, b) { + ( + MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id }, + MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id }, + ) if a_id == b_id => + MultiAsset { id: a_id, fun: Fungibility::Fungible(a_amount + b_amount) }, + ( + MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, + MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id }, + ) if a_id == b_id && a_instance == b_instance => + MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, + (to_push, to_remember) => { + res.push(to_push); + to_remember + }, + } + }); + res.push(last); + } + } + Self(res) + } +} + +impl> From for MultiAssets { + fn from(x: T) -> Self { + Self(vec![x.into()]) + } +} + +impl MultiAssets { + /// A new (empty) value. + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted and + /// which contain no duplicates. + /// + /// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. If you can't + /// guarantee that `r` is sorted and deduplicated, then use `From::>::from` which is infallible. + pub fn from_sorted_and_deduplicated(r: Vec) -> Result { + if r.is_empty() { + return Ok(Self(Vec::new())) + } + r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> { + if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { + Ok(b) + } else { + Err(()) + } + })?; + Ok(Self(r)) + } + + /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted and + /// which contain no duplicates. + /// + /// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation. + /// Generally though you should avoid using it unless you have a strict proof that `r` is valid. + #[cfg(test)] + pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { + Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped") + } + /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted and + /// which contain no duplicates. + /// + /// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation. + /// Generally though you should avoid using it unless you have a strict proof that `r` is valid. + /// + /// In test mode, this checks anyway and panics on fail. + #[cfg(not(test))] + pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { + Self(r) + } + + /// Add some asset onto the list, saturating. This is quite a laborious operation since it maintains the ordering. + pub fn push(&mut self, a: MultiAsset) { + if let Fungibility::Fungible(ref amount) = a.fun { + for asset in self.0.iter_mut().filter(|x| x.id == a.id) { + if let Fungibility::Fungible(ref mut balance) = asset.fun { + *balance = balance.saturating_add(*amount); + return + } + } + } + self.0.push(a); + self.0.sort(); + } + + /// Returns `true` if this definitely represents no asset. + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + + /// Returns true if `self` is a super-set of the given `inner`. + pub fn contains(&self, inner: &MultiAsset) -> bool { + self.0.iter().any(|i| i.contains(inner)) + } + + /// Consume `self` and return the inner vec. + pub fn drain(self) -> Vec { + self.0 + } + + /// Return a reference to the inner vec. + pub fn inner(&self) -> &Vec { + &self.0 + } + + /// Return the number of distinct asset instances contained. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.reanchor(prepend)) + } + + /// Return a reference to an item at a specific index or `None` if it doesn't exist. + pub fn get(&self, index: usize) -> Option<&MultiAsset> { + self.0.get(index) + } +} /// A wildcard representing a set of assets. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] @@ -55,24 +337,26 @@ pub enum WildMultiAsset { }, } -impl From for WildMultiAsset { - fn from(old: OldWildMultiAsset) -> WildMultiAsset { +impl TryFrom for WildMultiAsset { + type Error = (); + fn try_from(old: OldWildMultiAsset) -> Result { use OldWildMultiAsset::*; - match old { - AllOf { id, fun } => Self::AllOf { id, fun }, + Ok(match old { + AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun }, All => Self::All, - } + }) } } -impl From<(OldWildMultiAsset, u32)> for WildMultiAsset { - fn from(old: (OldWildMultiAsset, u32)) -> WildMultiAsset { +impl TryFrom<(OldWildMultiAsset, u32)> for WildMultiAsset { + type Error = (); + fn try_from(old: (OldWildMultiAsset, u32)) -> Result { use OldWildMultiAsset::*; let count = old.1; - match old.0 { - AllOf { id, fun } => Self::AllOfCounted { id, fun, count }, + Ok(match old.0 { + AllOf { id, fun } => Self::AllOfCounted { id: id.try_into()?, fun, count }, All => Self::AllCounted(count), - } + }) } } @@ -204,12 +488,13 @@ impl MultiAssetFilter { } } -impl From for MultiAssetFilter { - fn from(old: OldMultiAssetFilter) -> MultiAssetFilter { - match old { - OldMultiAssetFilter::Definite(x) => Self::Definite(x.into()), - OldMultiAssetFilter::Wild(x) => Self::Wild(x.into()), - } +impl TryFrom for MultiAssetFilter { + type Error = (); + fn try_from(old: OldMultiAssetFilter) -> Result { + Ok(match old { + OldMultiAssetFilter::Definite(x) => Self::Definite(x.try_into()?), + OldMultiAssetFilter::Wild(x) => Self::Wild(x.try_into()?), + }) } } @@ -218,8 +503,8 @@ impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter { fn try_from(old: (OldMultiAssetFilter, u32)) -> Result { let count = old.1; Ok(match old.0 { - OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 => Self::Definite(x.into()), - OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).into()), + OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 => Self::Definite(x.try_into()?), + OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).try_into()?), _ => return Err(()), }) } diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index cd9bed5fa489..7b5be8c2f0d4 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -439,4 +439,7 @@ pub trait XcmWeightInfo { fn clear_transact_status() -> Weight { 0 } + fn universal_origin(_: &Junction) -> Weight { + 0 + } } diff --git a/xcm/xcm-builder/src/location_conversion.rs b/xcm/xcm-builder/src/location_conversion.rs index a6933b52668f..4586633b6dd7 100644 --- a/xcm/xcm-builder/src/location_conversion.rs +++ b/xcm/xcm-builder/src/location_conversion.rs @@ -19,11 +19,11 @@ use parity_scale_codec::Encode; use sp_io::hashing::blake2_256; use sp_runtime::traits::AccountIdConversion; use sp_std::{borrow::Borrow, marker::PhantomData}; -use xcm::latest::{Junction::*, Junctions::*, MultiLocation, NetworkId, Parent}; +use xcm::latest::prelude::*; use xcm_executor::traits::{Convert, InvertLocation}; pub struct Account32Hash(PhantomData<(Network, AccountId)>); -impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> +impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> Convert for Account32Hash { fn convert_ref(location: impl Borrow) -> Result { @@ -102,14 +102,14 @@ impl + Into + AccountIdConversion, AccountId: /// Extracts the `AccountId32` from the passed `location` if the network matches. pub struct AccountId32Aliases(PhantomData<(Network, AccountId)>); -impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> +impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> Convert for AccountId32Aliases { fn convert(location: MultiLocation) -> Result { let id = match location { MultiLocation { parents: 0, - interior: X1(AccountId32 { id, network: NetworkId::Any }), + interior: X1(AccountId32 { id, network: None }), } => id, MultiLocation { parents: 0, interior: X1(AccountId32 { id, network }) } if network == Network::get() => @@ -125,14 +125,14 @@ impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone } pub struct AccountKey20Aliases(PhantomData<(Network, AccountId)>); -impl, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone> +impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone> Convert for AccountKey20Aliases { fn convert(location: MultiLocation) -> Result { let key = match location { MultiLocation { parents: 0, - interior: X1(AccountKey20 { key, network: NetworkId::Any }), + interior: X1(AccountKey20 { key, network: None }), } => key, MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network }) } if network == Network::get() => @@ -161,41 +161,30 @@ impl, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone /// ``` /// ```rust /// # use frame_support::parameter_types; -/// # use xcm::latest::{MultiLocation, Junction::*, Junctions::{self, *}, NetworkId::Any}; +/// # use xcm::latest::prelude::*; /// # use xcm_builder::LocationInverter; /// # use xcm_executor::traits::InvertLocation; /// # fn main() { /// parameter_types!{ -/// pub Ancestry: MultiLocation = X2( +/// pub Ancestry: InteriorMultiLocation = X2( /// Parachain(1), -/// AccountKey20 { network: Any, key: Default::default() }, -/// ).into(); +/// AccountKey20 { network: None, key: Default::default() }, +/// ); /// } /// -/// let input = MultiLocation::new(2, X2(Parachain(2), AccountId32 { network: Any, id: Default::default() })); +/// let input = MultiLocation::new(2, X2(Parachain(2), AccountId32 { network: None, id: Default::default() })); /// let inverted = LocationInverter::::invert_location(&input); /// assert_eq!(inverted, Ok(MultiLocation::new( /// 2, -/// X2(Parachain(1), AccountKey20 { network: Any, key: Default::default() }), +/// X2(Parachain(1), AccountKey20 { network: None, key: Default::default() }), /// ))); /// # } /// ``` pub struct LocationInverter(PhantomData); -impl> InvertLocation for LocationInverter { - fn ancestry() -> MultiLocation { +impl> InvertLocation for LocationInverter { + fn universal_location() -> InteriorMultiLocation { Ancestry::get() } - fn invert_location(location: &MultiLocation) -> Result { - let mut ancestry = Ancestry::get(); - let mut junctions = Here; - for _ in 0..location.parent_count() { - junctions = junctions - .pushed_with(ancestry.take_first_interior().unwrap_or(OnlyChild)) - .map_err(|_| ())?; - } - let parents = location.interior().len() as u8; - Ok(MultiLocation::new(parents, junctions)) - } } #[cfg(test)] @@ -203,14 +192,14 @@ mod tests { use super::*; use frame_support::parameter_types; - use xcm::latest::{Junction, NetworkId::Any}; + use xcm::latest::Junction; fn account20() -> Junction { - AccountKey20 { network: Any, key: Default::default() } + AccountKey20 { network: None, key: Default::default() } } fn account32() -> Junction { - AccountId32 { network: Any, id: Default::default() } + AccountId32 { network: None, id: Default::default() } } // Network Topology @@ -228,7 +217,7 @@ mod tests { #[test] fn inverter_works_in_tree() { parameter_types! { - pub Ancestry: MultiLocation = X3(Parachain(1), account20(), account20()).into(); + pub Ancestry: InteriorMultiLocation = X3(Parachain(1), account20(), account20()); } let input = MultiLocation::new(3, X2(Parachain(2), account32())); @@ -243,7 +232,7 @@ mod tests { #[test] fn inverter_uses_ancestry_as_inverted_location() { parameter_types! { - pub Ancestry: MultiLocation = X2(account20(), account20()).into(); + pub Ancestry: InteriorMultiLocation = X2(account20(), account20()); } let input = MultiLocation::grandparent(); @@ -258,7 +247,7 @@ mod tests { #[test] fn inverter_uses_only_child_on_missing_ancestry() { parameter_types! { - pub Ancestry: MultiLocation = X1(PalletInstance(5)).into(); + pub Ancestry: InteriorMultiLocation = X1(PalletInstance(5)); } let input = MultiLocation::grandparent(); @@ -269,7 +258,7 @@ mod tests { #[test] fn inverter_errors_when_location_is_too_large() { parameter_types! { - pub Ancestry: MultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here; } let input = MultiLocation { parents: 99, interior: X1(Parachain(88)) }; diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index d88df21050b6..783f367c7593 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -183,6 +183,7 @@ impl ConvertOrigin for TestOriginConverter { thread_local! { pub static IS_RESERVE: RefCell>> = RefCell::new(BTreeMap::new()); pub static IS_TELEPORTER: RefCell>> = RefCell::new(BTreeMap::new()); + pub static UNIVERSAL_ALIASES: RefCell> = RefCell::new(BTreeSet::new()); } pub fn add_reserve(from: MultiLocation, asset: MultiAssetFilter) { IS_RESERVE.with(|r| r.borrow_mut().entry(from).or_default().push(asset)); @@ -191,6 +192,10 @@ pub fn add_reserve(from: MultiLocation, asset: MultiAssetFilter) { pub fn add_teleporter(from: MultiLocation, asset: MultiAssetFilter) { IS_TELEPORTER.with(|r| r.borrow_mut().entry(from).or_default().push(asset)); } +#[allow(dead_code)] +pub fn add_universal_alias(bridge: MultiLocation, consensus: Junction) { + UNIVERSAL_ALIASES.with(|r| r.borrow_mut().insert((bridge, consensus))); +} pub struct TestIsReserve; impl FilterAssetLocation for TestIsReserve { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { @@ -206,7 +211,13 @@ impl FilterAssetLocation for TestIsTeleporter { } } -use xcm::latest::Response; +pub struct TestUniversalAliases; +impl Contains<(MultiLocation, Junction)> for TestUniversalAliases { + fn contains(t: &(MultiLocation, Junction)) -> bool { + UNIVERSAL_ALIASES.with(|r| r.borrow().contains(t)) + } +} + pub enum ResponseSlot { Expecting(MultiLocation), Received(Response), @@ -251,7 +262,7 @@ pub fn response(query_id: u64) -> Option { } parameter_types! { - pub TestAncestry: MultiLocation = X1(Parachain(42)).into(); + pub TestAncestry: InteriorMultiLocation = X1(Parachain(42)); pub UnitWeightCost: Weight = 10; } parameter_types! { @@ -290,4 +301,5 @@ impl Config for TestConfig { type SubscriptionService = TestSubscriptionService; type PalletInstancesInfo = TestPalletsInfo; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = TestUniversalAliases; } diff --git a/xcm/xcm-builder/src/origin_conversion.rs b/xcm/xcm-builder/src/origin_conversion.rs index b42c8fcff9ef..cb1894d0d886 100644 --- a/xcm/xcm-builder/src/origin_conversion.rs +++ b/xcm/xcm-builder/src/origin_conversion.rs @@ -177,7 +177,7 @@ impl, Origin> ConvertOrigin } pub struct SignedAccountId32AsNative(PhantomData<(Network, Origin)>); -impl, Origin: OriginTrait> ConvertOrigin +impl>, Origin: OriginTrait> ConvertOrigin for SignedAccountId32AsNative where Origin::AccountId: From<[u8; 32]>, @@ -196,7 +196,7 @@ where ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::AccountId32 { id, network }) }, - ) if matches!(network, NetworkId::Any) || network == Network::get() => + ) if matches!(network, None) || network == Network::get() => Ok(Origin::signed(id.into())), (_, origin) => Err(origin), } @@ -204,7 +204,7 @@ where } pub struct SignedAccountKey20AsNative(PhantomData<(Network, Origin)>); -impl, Origin: OriginTrait> ConvertOrigin +impl>, Origin: OriginTrait> ConvertOrigin for SignedAccountKey20AsNative where Origin::AccountId: From<[u8; 20]>, @@ -223,7 +223,7 @@ where ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::AccountKey20 { key, network }) }, - ) if (matches!(network, NetworkId::Any) || network == Network::get()) => + ) if (matches!(network, None) || network == Network::get()) => Ok(Origin::signed(key.into())), (_, origin) => Err(origin), } @@ -265,7 +265,7 @@ where pub struct SignedToAccountId32( PhantomData<(Origin, AccountId, Network)>, ); -impl, Network: Get> +impl, Network: Get>> Convert for SignedToAccountId32 where Origin::PalletsOrigin: From> diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index bbcc4fe14bbe..85f5c78396a7 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -16,7 +16,6 @@ use super::{mock::*, test_utils::*, *}; use frame_support::{assert_err, weights::constants::WEIGHT_PER_SECOND}; -use xcm::latest::{prelude::*, MaybeErrorCode, PalletInfo, QueryResponseInfo}; use xcm_executor::{traits::*, Config, XcmExecutor}; #[test] @@ -32,11 +31,11 @@ fn basic_setup_works() { assert_eq!(to_account(MultiLocation::new(1, X1(Parachain(1)))), Ok(2001)); assert_eq!(to_account(MultiLocation::new(1, X1(Parachain(50)))), Ok(2050)); assert_eq!( - to_account(MultiLocation::new(0, X1(AccountIndex64 { index: 1, network: Any }))), + to_account(MultiLocation::new(0, X1(AccountIndex64 { index: 1, network: None }))), Ok(1), ); assert_eq!( - to_account(MultiLocation::new(0, X1(AccountIndex64 { index: 42, network: Any }))), + to_account(MultiLocation::new(0, X1(AccountIndex64 { index: 42, network: None }))), Ok(42), ); assert_eq!(to_account(Here.into()), Ok(3000)); @@ -173,7 +172,7 @@ fn transfer_should_work() { Parachain(1), Xcm(vec![TransferAsset { assets: (Here, 100).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }]), 50, ); @@ -197,7 +196,7 @@ fn basic_asset_trap_should_work() { WithdrawAsset((Here, 100).into()), DepositAsset { assets: Wild(AllCounted(0)), // <<< 0 is an error. - beneficiary: AccountIndex64 { index: 3, network: Any }.into(), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), }, ]), 20, @@ -214,7 +213,7 @@ fn basic_asset_trap_should_work() { ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(1).into() }, DepositAsset { assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: Any }.into(), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), }, ]), 20, @@ -232,7 +231,7 @@ fn basic_asset_trap_should_work() { ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() }, DepositAsset { assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: Any }.into(), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), }, ]), 20, @@ -250,7 +249,7 @@ fn basic_asset_trap_should_work() { ClaimAsset { assets: (Here, 101).into(), ticket: GeneralIndex(0).into() }, DepositAsset { assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: Any }.into(), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), }, ]), 20, @@ -266,7 +265,7 @@ fn basic_asset_trap_should_work() { ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() }, DepositAsset { assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: Any }.into(), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), }, ]), 20, @@ -282,7 +281,7 @@ fn basic_asset_trap_should_work() { ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() }, DepositAsset { assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: Any }.into(), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), }, ]), 20, @@ -300,17 +299,17 @@ fn errors_should_return_unused_weight() { // First xfer results in an error on the last message only TransferAsset { assets: (Here, 1).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }, // Second xfer results in error third message and after TransferAsset { assets: (Here, 2).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }, // Third xfer results in error second message and after TransferAsset { assets: (Here, 4).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }, ]); // Weight limit of 70 is needed. @@ -378,7 +377,7 @@ fn code_registers_should_work() { SetErrorHandler(Xcm(vec![ TransferAsset { assets: (Here, 2).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }, // It was handled fine. ClearError, @@ -386,17 +385,17 @@ fn code_registers_should_work() { // Set the appendix - this will always fire. SetAppendix(Xcm(vec![TransferAsset { assets: (Here, 4).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }])), // First xfer always works ok TransferAsset { assets: (Here, 1).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }, // Second xfer results in error on the second message - our error handler will fire. TransferAsset { assets: (Here, 8).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), }, ]); // Weight limit of 70 is needed. @@ -422,7 +421,7 @@ fn reserve_transfer_should_work() { // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(1001, (Here, 1000)); // The remote account owned by gav. - let three: MultiLocation = X1(AccountIndex64 { index: 3, network: Any }).into(); + let three: MultiLocation = X1(AccountIndex64 { index: 3, network: None }).into(); // They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2 // and let them know to hand it to account #3. @@ -484,7 +483,7 @@ fn simple_version_subscriptions_should_work() { fn version_subscription_instruction_should_work() { let origin = Parachain(1000).into(); let message = Xcm::(vec![ - DescendOrigin(X1(AccountIndex64 { index: 1, network: Any })), + DescendOrigin(X1(AccountIndex64 { index: 1, network: None })), SubscribeVersion { query_id: 42, max_response_weight: 5000 }, ]); let weight_limit = 20; @@ -540,7 +539,7 @@ fn version_unsubscription_instruction_should_work() { // Not allowed to do it when origin has been changed. let message = Xcm::(vec![ - DescendOrigin(X1(AccountIndex64 { index: 1, network: Any })), + DescendOrigin(X1(AccountIndex64 { index: 1, network: None })), UnsubscribeVersion, ]); let weight_limit = 20; @@ -610,7 +609,7 @@ fn transacting_should_refund_weight() { #[test] fn paid_transacting_should_refund_payment_for_unused_weight() { - let one: MultiLocation = X1(AccountIndex64 { index: 1, network: Any }).into(); + let one: MultiLocation = X1(AccountIndex64 { index: 1, network: None }).into(); AllowPaidFrom::set(vec![one.clone()]); add_asset(1, (Parent, 100)); WeightPrice::set((Parent.into(), 1_000_000_000_000)); diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index d2b2152aedb7..c48086f94e16 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -116,7 +116,7 @@ impl configuration::Config for Runtime { parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::here(); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub Ancestry: MultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here.into(); pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -176,6 +176,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index aff5a129d633..18a54dd13bdc 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -20,10 +20,10 @@ use crate::traits::{ }; use frame_support::{ dispatch::{Dispatchable, Parameter}, - traits::{Get, PalletsInfoAccess}, + traits::{Get, PalletsInfoAccess, Contains}, weights::{GetDispatchInfo, PostDispatchInfo}, }; -use xcm::latest::SendXcm; +use xcm::latest::{MultiLocation, Junction, SendXcm}; /// The trait to parameterize the `XcmExecutor`. pub trait Config { @@ -78,4 +78,8 @@ pub trait Config { /// NOTE: In the worse case, the Holding Register may contain up to twice as many assets as this /// and any benchmarks should take that into account. type MaxAssetsIntoHolding: Get; + + /// The origin locations and specific universal junctions to which they are allowed to elevate + /// themselves. + type UniversalAliases: Contains<(MultiLocation, Junction)>; } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 33fc376e39c2..101ba34b3c23 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -19,18 +19,13 @@ use frame_support::{ dispatch::{Dispatchable, Weight}, ensure, - traits::{Get, PalletsInfoAccess}, + traits::{Get, PalletsInfoAccess, Contains}, weights::GetDispatchInfo, }; use parity_scale_codec::Encode; use sp_runtime::traits::Saturating; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::latest::{ - Error as XcmError, ExecuteXcm, - Instruction::{self, *}, - MaybeErrorCode, MultiAsset, MultiAssets, MultiLocation, Outcome, PalletInfo, QueryResponseInfo, - Response, SendXcm, Xcm, -}; +use xcm::latest::prelude::*; pub mod traits; use traits::{ @@ -572,6 +567,16 @@ impl XcmExecutor { self.transact_status = Default::default(); Ok(()) }, + UniversalOrigin(j) => { + let universal_location = Config::LocationInverter::universal_location(); + ensure!(universal_location.first() != Some(&j), XcmError::InvalidLocation); + let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let new_origin = AncestorThen(universal_location.len() as u8, X1(j.clone())).into(); + let ok = Config::UniversalAliases::contains(&(origin, j)); + ensure!(ok, XcmError::InvalidLocation); + self.origin = Some(new_origin); + Ok(()) + }, ExchangeAsset { .. } => Err(XcmError::Unimplemented), HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented), HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented), diff --git a/xcm/xcm-executor/src/traits/conversion.rs b/xcm/xcm-executor/src/traits/conversion.rs index d8c50037662d..d82ccfd8e3dd 100644 --- a/xcm/xcm-executor/src/traits/conversion.rs +++ b/xcm/xcm-executor/src/traits/conversion.rs @@ -16,7 +16,7 @@ use parity_scale_codec::{Decode, Encode}; use sp_std::{borrow::Borrow, convert::TryFrom, prelude::*, result::Result}; -use xcm::latest::{MultiLocation, OriginKind}; +use xcm::latest::prelude::*; /// Generic third-party conversion trait. Use this when you don't want to force the user to use default /// implementations of `From` and `Into` for the types you wish to convert between. @@ -204,9 +204,25 @@ impl ConvertOrigin for Tuple { } } -/// Means of inverting a location: given a location which describes a `target` interpreted from the -/// `source`, this will provide the corresponding location which describes the `source`. +/// Means of wotking with a relative location. pub trait InvertLocation { - fn ancestry() -> MultiLocation; - fn invert_location(l: &MultiLocation) -> Result; + /// Return the location of the local consensus system from the point of view of the location + /// `l`. + /// + /// Given a target `location`, the result provides a the location which represents the local + /// consensus system from the targets perspective. + fn invert_location(location: &MultiLocation) -> Result { + let mut ancestry = Self::universal_location(); + let mut junctions = Here; + for _ in 0..location.parent_count() { + junctions = junctions + .pushed_with(ancestry.take_first().unwrap_or(OnlyChild)) + .map_err(|_| ())?; + } + let parents = location.interior().len() as u8; + Ok(MultiLocation::new(parents, junctions)) + } + + /// Return the location of the local consensus system within the known Universe. + fn universal_location() -> InteriorMultiLocation; } diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index e0e87899da99..d7b926dd0156 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -207,7 +207,7 @@ mod tests { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), Box::new(X1(Parachain(1)).into().into()), - Box::new(X1(AccountId32 { network: Any, id: ALICE.into() }).into().into()), + Box::new(X1(AccountId32 { network: None, id: ALICE.into() }).into().into()), Box::new((Here, withdraw_amount).into()), 0, )); diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 45c36fbb280a..97e337512940 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -104,7 +104,7 @@ parameter_types! { parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); + pub Ancestry: InteriorMultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); } pub type LocationToAccountId = ( @@ -150,6 +150,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } #[frame_support::pallet] diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 8ad503cc6417..0c7ef43ed40a 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -97,7 +97,7 @@ parameter_types! { pub const KsmLocation: MultiLocation = Here.into(); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here.into(); pub UnitWeightCost: Weight = 1_000; } @@ -142,6 +142,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/xcm/xcm-simulator/fuzzer/src/parachain.rs b/xcm/xcm-simulator/fuzzer/src/parachain.rs index b5395af7be2b..025dfb4de864 100644 --- a/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -104,7 +104,7 @@ parameter_types! { parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); + pub Ancestry: InteriorMultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); } pub type LocationToAccountId = ( @@ -150,6 +150,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } #[frame_support::pallet] diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 8ad503cc6417..0c7ef43ed40a 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -97,7 +97,7 @@ parameter_types! { pub const KsmLocation: MultiLocation = Here.into(); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here.into(); pub UnitWeightCost: Weight = 1_000; } @@ -142,6 +142,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type UniversalAliases = Nothing; } pub type LocalOriginToLocation = SignedToAccountId32; From 535980e4a261743a556ede10a4377b212ba86b9c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 10 Jan 2022 11:35:51 +0100 Subject: [PATCH 02/57] Missing bit of cherry-pick --- runtime/test-runtime/src/xcm_config.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 6ce7498ee6a6..53322fdde954 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -72,9 +72,7 @@ impl InvertLocation for InvertNothing { fn invert_location(_: &MultiLocation) -> sp_std::result::Result { Ok(Here.into()) } - fn ancestry() -> MultiLocation { - Here.into() - } + fn universal_location() -> InteriorMultiLocation { Ok(Here.into()) } From d4376f19c05cf17e437caa6b97b12ea7e822c686 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 14:18:15 +0100 Subject: [PATCH 03/57] Revamped XCM proc macros; new NetworkIds --- runtime/kusama/src/lib.rs | 10 +++++----- runtime/rococo/src/lib.rs | 10 +++++----- runtime/test-runtime/src/lib.rs | 2 +- runtime/test-runtime/src/xcm_config.rs | 4 ++-- runtime/westend/src/lib.rs | 10 +++++----- xcm/pallet-xcm-benchmarks/src/fungible/mock.rs | 2 +- xcm/pallet-xcm-benchmarks/src/lib.rs | 2 +- xcm/pallet-xcm/src/mock.rs | 2 +- xcm/pallet-xcm/src/tests.rs | 12 ++++++------ xcm/procedural/src/lib.rs | 7 +++++++ xcm/src/v0/junction.rs | 3 ++- xcm/src/v3/mod.rs | 4 +++- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 2 +- 14 files changed, 41 insertions(+), 31 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 9afafe1fed49..1b3ce5f9d4a3 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1322,11 +1322,11 @@ parameter_types! { /// the context". pub const KsmLocation: MultiLocation = Here.into(); /// The Kusama network ID. This is named. - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const ThisNetwork: NetworkId = Kusama; /// Our XCM location ancestry - i.e. our location within the Consensus Universe. /// /// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID. - pub const Ancestry: InteriorMultiLocation = X1(GlobalConsensus(NetworkId::Kusama)); + pub const Ancestry: InteriorMultiLocation = ThisNetwork.into(); /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -1337,7 +1337,7 @@ pub type SovereignAccountOf = ( // We can convert a child parachain using the standard `AccountId` conversion. ChildParachainConvertsVia, // We can directly alias an `AccountId32` into a local account. - AccountId32Aliases, + AccountId32Aliases, ); /// Our asset transactor. This is what allows us to interest with the runtime facilities from the point of @@ -1364,7 +1364,7 @@ type LocalOriginConverter = ( // A child parachain, natively expressed, has the `Parachain` origin. ChildParachainAsNative, // The AccountId32 location type can be expressed natively as a `Signed` origin. - SignedAccountId32AsNative, + SignedAccountId32AsNative, // A system child parachain, expressed as a Superuser, converts to the `Root` origin. ChildSystemParachainAsSuperuser, ); @@ -1450,7 +1450,7 @@ pub type LocalOriginToLocation = ( CouncilBodyId, >, // And a usual Signed origin to be used in XCM as a corresponding AccountId32 - SignedToAccountId32, + SignedToAccountId32, ); impl pallet_xcm::Config for Runtime { type Event = Event; diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 632d03c1bd48..0e9c8f12a21c 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -636,13 +636,13 @@ impl parachains_paras::Config for Runtime { parameter_types! { pub const RocLocation: MultiLocation = Here.into(); - pub const RococoNetwork: NetworkId = NetworkId::Polkadot; - pub const Ancestry: InteriorMultiLocation = GeneralKey(b"rococo".into()); + pub const ThisNetwork: NetworkId = Rococo; + pub const Ancestry: InteriorMultiLocation = GlobalConsensus(ThisNetwork); pub CheckAccount: AccountId = XcmPallet::check_account(); } pub type SovereignAccountOf = - (ChildParachainConvertsVia, AccountId32Aliases); + (ChildParachainConvertsVia, AccountId32Aliases); pub type LocalAssetTransactor = XcmCurrencyAdapter< // Use this currency: @@ -660,7 +660,7 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter< type LocalOriginConverter = ( SovereignSignedViaLocation, ChildParachainAsNative, - SignedAccountId32AsNative, + SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, ); @@ -750,7 +750,7 @@ pub type LocalOriginToLocation = ( // `Unit` body. BackingToPlurality, CollectiveBodyId>, // And a usual Signed origin to be used in XCM as a corresponding AccountId32 - SignedToAccountId32, + SignedToAccountId32, ); impl pallet_xcm::Config for Runtime { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 2583340c2c7f..34dc046e2907 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -520,7 +520,7 @@ impl parachains_ump::Config for Runtime { parameter_types! { pub const BaseXcmWeight: frame_support::weights::Weight = 1_000; - pub const AnyNetwork: xcm::latest::NetworkId = xcm::latest::NetworkId::Any; + pub const AnyNetwork: Option = None; pub const MaxInstructions: u32 = 100; } diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 53322fdde954..4c1dd26c587f 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use frame_support::{parameter_types, traits::Everything, weights::Weight}; +use frame_support::{parameter_types, traits::{Everything, Nothing}, weights::Weight}; use xcm::latest::prelude::*; use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, SignedToAccountId32}; use xcm_executor::{ @@ -74,7 +74,7 @@ impl InvertLocation for InvertNothing { } fn universal_location() -> InteriorMultiLocation { - Ok(Here.into()) + Here } } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index a64fd9f7ac73..ce707b173e72 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -981,13 +981,13 @@ impl auctions::Config for Runtime { parameter_types! { pub const WndLocation: MultiLocation = Here.into(); - pub const Ancestry: InteriorMultiLocation = GeneralKey(b"westend".into()); - pub WestendNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec()); + pub const ThisNetwork: NetworkId = Westend; + pub const Ancestry: InteriorMultiLocation = ThisNetwork.into(); pub CheckAccount: AccountId = XcmPallet::check_account(); } pub type LocationConverter = - (ChildParachainConvertsVia, AccountId32Aliases); + (ChildParachainConvertsVia, AccountId32Aliases); pub type LocalAssetTransactor = XcmCurrencyAdapter< // Use this currency: @@ -1005,7 +1005,7 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter< type LocalOriginConverter = ( SovereignSignedViaLocation, ChildParachainAsNative, - SignedAccountId32AsNative, + SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, ); @@ -1068,7 +1068,7 @@ impl xcm_executor::Config for XcmConfig { /// of this chain. pub type LocalOriginToLocation = ( // And a usual Signed origin to be used in XCM as a corresponding AccountId32 - SignedToAccountId32, + SignedToAccountId32, ); impl pallet_xcm::Config for Runtime { diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 5b7c2362b3c7..a351fa0e6104 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -153,7 +153,7 @@ impl crate::Config for Test { type AccountIdConverter = AccountIdConverter; fn valid_destination() -> Result { let valid_destination: MultiLocation = - X1(AccountId32 { network: NetworkId::Any, id: [0u8; 32] }).into(); + X1(AccountId32 { network: None, id: [0u8; 32] }).into(); Ok(valid_destination) } diff --git a/xcm/pallet-xcm-benchmarks/src/lib.rs b/xcm/pallet-xcm-benchmarks/src/lib.rs index a7a4e715db39..8db2496e0b7d 100644 --- a/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -99,7 +99,7 @@ fn account_id_junction(index: u32) -> Junction { encoded.resize(32, 0u8); let mut id = [0u8; 32]; id.copy_from_slice(&encoded); - Junction::AccountId32 { network: NetworkId::Any, id } + Junction::AccountId32 { network: None, id } } pub fn account_and_location(index: u32) -> (T::AccountId, MultiLocation) { diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 0f64ea44cda2..a3db242cb803 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -217,7 +217,7 @@ impl pallet_balances::Config for Test { parameter_types! { pub const RelayLocation: MultiLocation = Here.into(); - pub const AnyNetwork: NetworkId = NetworkId::Any; + pub const AnyNetwork: Option = None; pub Ancestry: InteriorMultiLocation = Here.into(); pub UnitWeightCost: Weight = 1_000; } diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index f902d53f899c..47604f130958 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -341,7 +341,7 @@ fn reserve_transfer_assets_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = BaseXcmWeight::get(); let dest: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }.into(); + Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::reserve_transfer_assets( Origin::signed(ALICE), @@ -387,7 +387,7 @@ fn limited_reserve_transfer_assets_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = BaseXcmWeight::get(); let dest: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }.into(); + Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( Origin::signed(ALICE), @@ -434,7 +434,7 @@ fn unlimited_reserve_transfer_assets_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = BaseXcmWeight::get(); let dest: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }.into(); + Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( Origin::signed(ALICE), @@ -479,7 +479,7 @@ fn execute_withdraw_to_deposit_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = 3 * BaseXcmWeight::get(); let dest: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: BOB.into() }.into(); + Junction::AccountId32 { network: None, id: BOB.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::execute( Origin::signed(ALICE), @@ -506,7 +506,7 @@ fn trapped_assets_can_be_claimed() { new_test_ext_with_balances(balances).execute_with(|| { let weight = 6 * BaseXcmWeight::get(); let dest: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: BOB.into() }.into(); + Junction::AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::execute( Origin::signed(ALICE), @@ -523,7 +523,7 @@ fn trapped_assets_can_be_claimed() { weight )); let source: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }.into(); + Junction::AccountId32 { network: None, id: ALICE.into() }.into(); let trapped = AssetTraps::::iter().collect::>(); let vma = VersionedMultiAssets::from(MultiAssets::from((Here, SEND_AMOUNT))); let hash = BlakeTwo256::hash_of(&(source.clone(), vma.clone())); diff --git a/xcm/procedural/src/lib.rs b/xcm/procedural/src/lib.rs index 1ad801341f03..d187967485e7 100644 --- a/xcm/procedural/src/lib.rs +++ b/xcm/procedural/src/lib.rs @@ -48,3 +48,10 @@ pub fn impl_conversion_functions_for_multilocation_v3(input: TokenStream) -> Tok .unwrap_or_else(syn::Error::into_compile_error) .into() } + +#[proc_macro] +pub fn impl_conversion_functions_for_junctions_v3(input: TokenStream) -> TokenStream { + v3::junctions::generate_conversion_functions(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/xcm/src/v0/junction.rs b/xcm/src/v0/junction.rs index f8af909e7a45..6f192ff4045b 100644 --- a/xcm/src/v0/junction.rs +++ b/xcm/src/v0/junction.rs @@ -41,9 +41,10 @@ impl TryInto for Option { use NewNetworkId::*; Ok(match self { None => NetworkId::Any, - Some(Named(name)) => NetworkId::Named(name), + Some(ByUri(name)) => NetworkId::Named(name), Some(Polkadot) => NetworkId::Polkadot, Some(Kusama) => NetworkId::Kusama, + _ => return Err(()), }) } } diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index ddba5da53471..6c7026933e81 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -31,6 +31,7 @@ use scale_info::TypeInfo; mod multiasset; mod traits; mod junction; +pub(crate) mod junctions; mod multilocation; pub use multiasset::{ @@ -41,8 +42,9 @@ pub use traits::{ Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo, }; pub use junction::{Junction, NetworkId}; +pub use junctions::{Junctions}; pub use multilocation::{ - Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen, + Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 0c7ef43ed40a..8e4a96a07ef1 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -96,7 +96,7 @@ impl configuration::Config for Runtime { parameter_types! { pub const KsmLocation: MultiLocation = Here.into(); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: NetworkId = NetworkId::Any; + pub const AnyNetwork: Option = None; pub Ancestry: InteriorMultiLocation = Here.into(); pub UnitWeightCost: Weight = 1_000; } diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 0c7ef43ed40a..8e4a96a07ef1 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -96,7 +96,7 @@ impl configuration::Config for Runtime { parameter_types! { pub const KsmLocation: MultiLocation = Here.into(); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: NetworkId = NetworkId::Any; + pub const AnyNetwork: Option = None; pub Ancestry: InteriorMultiLocation = Here.into(); pub UnitWeightCost: Weight = 1_000; } From f093c554671494df306e1584c9efaa2ec1f78bf1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 16:02:18 +0100 Subject: [PATCH 04/57] Fixes --- runtime/kusama/src/lib.rs | 22 +++--- runtime/rococo/src/lib.rs | 36 +++++----- runtime/test-runtime/src/lib.rs | 4 +- runtime/westend/src/lib.rs | 27 +++---- runtime/westend/src/tests.rs | 6 +- .../src/fungible/mock.rs | 8 +-- xcm/pallet-xcm/src/mock.rs | 11 +-- xcm/pallet-xcm/src/tests.rs | 70 +++++++++---------- xcm/src/lib.rs | 11 +-- xcm/src/v3/multiasset.rs | 7 ++ xcm/xcm-builder/src/tests.rs | 56 +++++++-------- xcm/xcm-builder/tests/mock/mod.rs | 6 +- xcm/xcm-builder/tests/scenarios.rs | 16 ++--- xcm/xcm-simulator/example/src/lib.rs | 4 +- xcm/xcm-simulator/example/src/parachain.rs | 2 +- xcm/xcm-simulator/example/src/relay_chain.rs | 16 ++--- xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 18 ++--- 17 files changed, 166 insertions(+), 154 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 1b3ce5f9d4a3..3918502771cd 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1320,13 +1320,13 @@ parameter_types! { /// The location of the KSM token, from the context of this chain. Since this token is native to this /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to /// the context". - pub const KsmLocation: MultiLocation = Here.into(); + pub const TokenLocation: MultiLocation = Here.into_location(); /// The Kusama network ID. This is named. pub const ThisNetwork: NetworkId = Kusama; /// Our XCM location ancestry - i.e. our location within the Consensus Universe. /// /// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID. - pub const Ancestry: InteriorMultiLocation = ThisNetwork.into(); + pub Ancestry: InteriorMultiLocation = ThisNetwork::get().into(); /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -1343,12 +1343,12 @@ pub type SovereignAccountOf = ( /// Our asset transactor. This is what allows us to interest with the runtime facilities from the point of /// view of XCM-only concepts like `MultiLocation` and `MultiAsset`. /// -/// Ours is only aware of the Balances pallet, which is mapped to `KsmLocation`. +/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`. pub type LocalAssetTransactor = XcmCurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + IsConcrete, // We can convert the MultiLocations with our converter above: SovereignAccountOf, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -1385,13 +1385,15 @@ pub type XcmRouter = ( ); parameter_types! { - pub const Kusama: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(KsmLocation::get()) }); - pub const KusamaForStatemine: (MultiAssetFilter, MultiLocation) = (Kusama::get(), Parachain(1000).into()); - pub const KusamaForEncointer: (MultiAssetFilter, MultiLocation) = (Kusama::get(), Parachain(1001).into()); + pub const Ksm: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); + pub const KsmForStatemine: (MultiAssetFilter, MultiLocation) = (Ksm::get(), Parachain(1000).into_location()); + pub const KsmForEncointer: (MultiAssetFilter, MultiLocation) = (Ksm::get(), Parachain(1001).into_location()); pub const MaxAssetsIntoHolding: u32 = 64; } -pub type TrustedTeleporters = - (xcm_builder::Case, xcm_builder::Case); +pub type TrustedTeleporters = ( + xcm_builder::Case, + xcm_builder::Case, +); match_type! { pub type OnlyParachains: impl Contains = { @@ -1425,7 +1427,7 @@ impl xcm_executor::Config for XcmConfig { type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. - type Trader = UsingComponents>; + type Trader = UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 0e9c8f12a21c..8d6e3cb07270 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -635,9 +635,9 @@ impl parachains_paras::Config for Runtime { } parameter_types! { - pub const RocLocation: MultiLocation = Here.into(); - pub const ThisNetwork: NetworkId = Rococo; - pub const Ancestry: InteriorMultiLocation = GlobalConsensus(ThisNetwork); + pub const TokenLocation: MultiLocation = Here.into_location(); + pub const ThisNetwork: NetworkId = NetworkId::Rococo; + pub Ancestry: InteriorMultiLocation = ThisNetwork::get().into(); pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -648,7 +648,7 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + IsConcrete, // We can convert the MultiLocations with our converter above: SovereignAccountOf, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -676,23 +676,23 @@ pub type XcmRouter = ( ); parameter_types! { - pub const Rococo: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(RocLocation::get()) }); - pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(100).into()); - pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(110).into()); - pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(120).into()); - pub const RococoForStatemine: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(1000).into()); - pub const RococoForCanvas: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(1002).into()); - pub const RococoForEncointer: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Parachain(1003).into()); + pub const Roc: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); + pub const RocForTick: (MultiAssetFilter, MultiLocation) = (Roc::get(), Parachain(100).into_location()); + pub const RocForTrick: (MultiAssetFilter, MultiLocation) = (Roc::get(), Parachain(110).into_location()); + pub const RocForTrack: (MultiAssetFilter, MultiLocation) = (Roc::get(), Parachain(120).into_location()); + pub const RocForRockmine: (MultiAssetFilter, MultiLocation) = (Roc::get(), Parachain(1000).into_location()); + pub const RocForCanvas: (MultiAssetFilter, MultiLocation) = (Roc::get(), Parachain(1002).into_location()); + pub const RocForEncointer: (MultiAssetFilter, MultiLocation) = (Roc::get(), Parachain(1003).into_location()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } pub type TrustedTeleporters = ( - xcm_builder::Case, - xcm_builder::Case, - xcm_builder::Case, - xcm_builder::Case, - xcm_builder::Case, - xcm_builder::Case, + xcm_builder::Case, + xcm_builder::Case, + xcm_builder::Case, + xcm_builder::Case, + xcm_builder::Case, + xcm_builder::Case, ); parameter_types! { @@ -729,7 +729,7 @@ impl xcm_executor::Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = UsingComponents>; + type Trader = UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 34dc046e2907..fb4b16877324 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -606,7 +606,7 @@ pub mod pallet_test_notifier { .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) .map_err(|_| Error::::BadAccountFormat)?; let qid = pallet_xcm::Pallet::::new_query( - Junction::AccountId32 { network: None, id }.into(), + Junction::AccountId32 { network: None, id }, 100u32.into(), ); Self::deposit_event(Event::::QueryPrepared(qid)); @@ -622,7 +622,7 @@ pub mod pallet_test_notifier { let call = Call::::notification_received { query_id: 0, response: Default::default() }; let qid = pallet_xcm::Pallet::::new_notify_query( - Junction::AccountId32 { network: None, id }.into(), + Junction::AccountId32 { network: None, id }, ::Call::from(call), 100u32.into(), ); diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index ce707b173e72..4281555247bb 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -980,9 +980,9 @@ impl auctions::Config for Runtime { } parameter_types! { - pub const WndLocation: MultiLocation = Here.into(); + pub const TokenLocation: MultiLocation = Here.into_location(); pub const ThisNetwork: NetworkId = Westend; - pub const Ancestry: InteriorMultiLocation = ThisNetwork.into(); + pub Ancestry: InteriorMultiLocation = ThisNetwork::get().into(); pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -993,7 +993,7 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + IsConcrete, // We can convert the MultiLocations with our converter above: LocationConverter, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -1017,17 +1017,18 @@ pub type XcmRouter = ( ); parameter_types! { - pub const Westmint: MultiLocation = Parachain(1000).into(); + pub const Westmint: MultiLocation = Parachain(1000).into_location(); pub const Encointer: MultiLocation = Parachain(1001).into(); - pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) = - (Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), Westmint::get()); - pub const WestendForEncointer: (MultiAssetFilter, MultiLocation) = - (Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), Encointer::get()); + pub const Wnd: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); + pub const WndForWestmint: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Westmint::get()); + pub const WndForEncointer: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Encointer::get()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } -pub type TrustedTeleporters = - (xcm_builder::Case, xcm_builder::Case); +pub type TrustedTeleporters = ( + xcm_builder::Case, + xcm_builder::Case, +); /// The barriers one of which must be passed for an XCM message to be executed. pub type Barrier = ( @@ -1054,7 +1055,7 @@ impl xcm_executor::Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = WeightInfoBounds, Call, MaxInstructions>; - type Trader = UsingComponents>; + type Trader = UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; @@ -1677,7 +1678,7 @@ sp_api::impl_runtime_apis! { parameter_types! { pub const TrustedTeleporter: Option<(MultiLocation, MultiAsset)> = Some(( Westmint::get(), - MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(WndLocation::get()) }, + MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(TokenLocation::get()) }, )); } @@ -1689,7 +1690,7 @@ sp_api::impl_runtime_apis! { fn get_multi_asset() -> MultiAsset { MultiAsset { - id: Concrete(WndLocation::get()), + id: Concrete(TokenLocation::get()), fun: Fungible(1 * UNITS), } } diff --git a/runtime/westend/src/tests.rs b/runtime/westend/src/tests.rs index 841089f93ead..04575e6fc8e6 100644 --- a/runtime/westend/src/tests.rs +++ b/runtime/westend/src/tests.rs @@ -57,9 +57,9 @@ fn sanity_check_teleport_assets_weight() { // so this test will certainly ensure that this problem does not occur. use frame_support::dispatch::GetDispatchInfo; let weight = pallet_xcm::Call::::teleport_assets { - dest: Box::new(xcm::VersionedMultiLocation::V1(MultiLocation::here())), - beneficiary: Box::new(xcm::VersionedMultiLocation::V1(MultiLocation::here())), - assets: Box::new((Concrete(MultiLocation::here()), Fungible(200_000)).into()), + dest: Box::new(Here.into()), + beneficiary: Box::new(Here.into()), + assets: Box::new((Here, 200_000).into()), fee_asset_item: 0, } .get_dispatch_info() diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index a351fa0e6104..5b5bef1b686a 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -18,7 +18,7 @@ use crate::{fungible as xcm_balances_benchmark, mock::*}; use frame_benchmarking::BenchmarkError; -use frame_support::{parameter_types, traits::Everything}; +use frame_support::{parameter_types, traits::{Everything, Nothing}}; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -169,13 +169,13 @@ pub type TrustedTeleporters = (xcm_builder::Case,); parameter_types! { pub const CheckedAccount: Option = Some(100); - pub const ChildTeleporter: MultiLocation = Parachain(1000).into(); + pub const ChildTeleporter: MultiLocation = Parachain(1000).into_location(); pub const TrustedTeleporter: Option<(MultiLocation, MultiAsset)> = Some(( ChildTeleporter::get(), - MultiAsset { id: Concrete(Here.into()), fun: Fungible(100) }, + MultiAsset { id: Concrete(Here.into_location()), fun: Fungible(100) }, )); pub const TeleConcreteFung: (MultiAssetFilter, MultiLocation) = - (Wild(AllOf { fun: WildFungible, id: Concrete(Here.into()) }), ChildTeleporter::get()); + (Wild(AllOf { fun: WildFungible, id: Concrete(Here.into_location()) }), ChildTeleporter::get()); } impl xcm_balances_benchmark::Config for Test { diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index a3db242cb803..f156e5ccb27a 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use frame_support::{construct_runtime, parameter_types, traits::Everything, weights::Weight}; +use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use frame_support::traits::{Nothing, Everything}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::origin; use sp_core::H256; @@ -80,7 +81,7 @@ pub mod pallet_test_notifier { .using_encoded(|mut d| <[u8; 32]>::decode(&mut d)) .map_err(|_| Error::::BadAccountFormat)?; let qid = crate::Pallet::::new_query( - Junction::AccountId32 { network: None, id }.into(), + Junction::AccountId32 { network: None, id }, 100u32.into(), ); Self::deposit_event(Event::::QueryPrepared(qid)); @@ -96,7 +97,7 @@ pub mod pallet_test_notifier { let call = Call::::notification_received { query_id: 0, response: Default::default() }; let qid = crate::Pallet::::new_notify_query( - Junction::AccountId32 { network: None, id }.into(), + Junction::AccountId32 { network: None, id }, ::Call::from(call), 100u32.into(), ); @@ -216,9 +217,9 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub const RelayLocation: MultiLocation = Here.into(); + pub const RelayLocation: MultiLocation = Here.into_location(); pub const AnyNetwork: Option = None; - pub Ancestry: InteriorMultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here; pub UnitWeightCost: Weight = 1_000; } diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 47604f130958..32ff19154879 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -39,7 +39,7 @@ const SEND_AMOUNT: u128 = 10; fn report_outcome_notify_works() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; - let sender = AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); + let sender: MultiLocation = AccountId32 { network: None, id: ALICE.into() }.into(); let mut message = Xcm(vec![TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone(), @@ -50,7 +50,7 @@ fn report_outcome_notify_works() { }; let notify = Call::TestNotifier(call); new_test_ext_with_balances(balances).execute_with(|| { - XcmPallet::report_outcome_notify(&mut message, Parachain(PARA_ID).into(), notify, 100) + XcmPallet::report_outcome_notify(&mut message, Parachain(PARA_ID).into_location(), notify, 100) .unwrap(); assert_eq!( message, @@ -71,7 +71,7 @@ fn report_outcome_notify_works() { assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![QueryResponse { query_id: 0, response: Response::ExecutionResult(None), @@ -99,13 +99,13 @@ fn report_outcome_notify_works() { fn report_outcome_works() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; - let sender = AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); + let sender: MultiLocation = AccountId32 { network: None, id: ALICE.into() }.into(); let mut message = Xcm(vec![TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone(), }]); new_test_ext_with_balances(balances).execute_with(|| { - XcmPallet::report_outcome(&mut message, Parachain(PARA_ID).into(), 100).unwrap(); + XcmPallet::report_outcome(&mut message, Parachain(PARA_ID).into_location(), 100).unwrap(); assert_eq!( message, Xcm(vec![ @@ -125,7 +125,7 @@ fn report_outcome_works() { assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![QueryResponse { query_id: 0, response: Response::ExecutionResult(None), @@ -153,7 +153,7 @@ fn send_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let sender: MultiLocation = - AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); + AccountId32 { network: None, id: ALICE.into() }.into(); let message = Xcm(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), ClearOrigin, @@ -190,7 +190,7 @@ fn send_fails_when_xcm_router_blocks() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let sender: MultiLocation = - Junction::AccountId32 { network: NoneNetwork::get(), id: ALICE.into() }.into(); + Junction::AccountId32 { network: None, id: ALICE.into() }.into(); let message = Xcm(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), buy_execution((Parent, SEND_AMOUNT)), @@ -345,7 +345,7 @@ fn reserve_transfer_assets_works() { assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::reserve_transfer_assets( Origin::signed(ALICE), - Box::new(Parachain(PARA_ID).into().into()), + Box::new(Parachain(PARA_ID).into()), Box::new(dest.clone().into()), Box::new((Here, SEND_AMOUNT).into()), 0, @@ -391,7 +391,7 @@ fn limited_reserve_transfer_assets_works() { assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( Origin::signed(ALICE), - Box::new(Parachain(PARA_ID).into().into()), + Box::new(Parachain(PARA_ID).into()), Box::new(dest.clone().into()), Box::new((Here, SEND_AMOUNT).into()), 0, @@ -438,7 +438,7 @@ fn unlimited_reserve_transfer_assets_works() { assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( Origin::signed(ALICE), - Box::new(Parachain(PARA_ID).into().into()), + Box::new(Parachain(PARA_ID).into()), Box::new(dest.clone().into()), Box::new((Here, SEND_AMOUNT).into()), 0, @@ -580,15 +580,15 @@ fn trapped_assets_can_be_claimed() { #[test] fn fake_latest_versioned_multilocation_works() { use codec::Encode; - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); let versioned_remote = LatestVersionedMultiLocation(&remote); - assert_eq!(versioned_remote.encode(), VersionedMultiLocation::from(remote.clone()).encode()); + assert_eq!(versioned_remote.encode(), remote.into_versioned().encode()); } #[test] fn basic_subscription_works() { new_test_ext_with_balances(vec![]).execute_with(|| { - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); assert_ok!(XcmPallet::force_subscribe_version_notify( Origin::root(), Box::new(remote.clone().into()), @@ -631,13 +631,13 @@ fn basic_subscription_works() { #[test] fn subscriptions_increment_id() { new_test_ext_with_balances(vec![]).execute_with(|| { - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); assert_ok!(XcmPallet::force_subscribe_version_notify( Origin::root(), Box::new(remote.clone().into()), )); - let remote2 = Parachain(1001).into(); + let remote2: MultiLocation = Parachain(1001).into(); assert_ok!(XcmPallet::force_subscribe_version_notify( Origin::root(), Box::new(remote2.clone().into()), @@ -662,7 +662,7 @@ fn subscriptions_increment_id() { #[test] fn double_subscription_fails() { new_test_ext_with_balances(vec![]).execute_with(|| { - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); assert_ok!(XcmPallet::force_subscribe_version_notify( Origin::root(), Box::new(remote.clone().into()), @@ -680,7 +680,7 @@ fn double_subscription_fails() { #[test] fn unsubscribe_works() { new_test_ext_with_balances(vec![]).execute_with(|| { - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); assert_ok!(XcmPallet::force_subscribe_version_notify( Origin::root(), Box::new(remote.clone().into()), @@ -716,7 +716,7 @@ fn subscription_side_works() { new_test_ext_with_balances(vec![]).execute_with(|| { AdvertisedXcmVersion::set(1); - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); let weight = BaseXcmWeight::get(); let message = Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]); let r = XcmExecutor::::execute_xcm(remote.clone(), message, weight); @@ -750,9 +750,9 @@ fn subscription_side_upgrades_work_with_notify() { let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000)); let v0_location = VersionedMultiLocation::from(v0_location); VersionNotifyTargets::::insert(0, v0_location, (69, 0, 1)); - let v1_location = Parachain(1001).into().versioned(); + let v1_location = Parachain(1001).into_versioned(); VersionNotifyTargets::::insert(1, v1_location, (70, 0, 1)); - let v2_location = Parachain(1002).into().versioned(); + let v2_location = Parachain(1002).into_versioned(); VersionNotifyTargets::::insert(2, v2_location, (71, 0, 1)); // New version. @@ -784,9 +784,9 @@ fn subscription_side_upgrades_work_with_notify() { assert_eq!( contents, vec![ - (XCM_VERSION, Parachain(1000).into().versioned(), (69, 0, 2)), - (XCM_VERSION, Parachain(1001).into().versioned(), (70, 0, 2)), - (XCM_VERSION, Parachain(1002).into().versioned(), (71, 0, 2)), + (XCM_VERSION, Parachain(1000).into_versioned(), (69, 0, 2)), + (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 2)), + (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 2)), ] ); }); @@ -799,9 +799,9 @@ fn subscription_side_upgrades_work_without_notify() { let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000)); let v0_location = VersionedMultiLocation::from(v0_location); VersionNotifyTargets::::insert(0, v0_location, (69, 0, 2)); - let v1_location = Parachain(1001).into().versioned(); + let v1_location = Parachain(1001).into_versioned(); VersionNotifyTargets::::insert(1, v1_location, (70, 0, 2)); - let v2_location = Parachain(1002).into().versioned(); + let v2_location = Parachain(1002).into_versioned(); VersionNotifyTargets::::insert(2, v2_location, (71, 0, 2)); // A runtime upgrade which alters the version does send notifications. @@ -813,9 +813,9 @@ fn subscription_side_upgrades_work_without_notify() { assert_eq!( contents, vec![ - (XCM_VERSION, Parachain(1000).into().versioned(), (69, 0, 2)), - (XCM_VERSION, Parachain(1001).into().versioned(), (70, 0, 2)), - (XCM_VERSION, Parachain(1002).into().versioned(), (71, 0, 2)), + (XCM_VERSION, Parachain(1000).into_versioned(), (69, 0, 2)), + (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 2)), + (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 2)), ] ); }); @@ -824,7 +824,7 @@ fn subscription_side_upgrades_work_without_notify() { #[test] fn subscriber_side_subscription_works() { new_test_ext_with_balances(vec![]).execute_with(|| { - let remote = Parachain(1000).into(); + let remote: MultiLocation = Parachain(1000).into(); assert_ok!(XcmPallet::force_subscribe_version_notify( Origin::root(), Box::new(remote.clone().into()), @@ -948,9 +948,9 @@ fn subscription_side_upgrades_work_with_multistage_notify() { let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000)); let v0_location = VersionedMultiLocation::from(v0_location); VersionNotifyTargets::::insert(0, v0_location, (69, 0, 1)); - let v1_location = Parachain(1001).into().versioned(); + let v1_location = Parachain(1001).into_versioned(); VersionNotifyTargets::::insert(1, v1_location, (70, 0, 1)); - let v2_location = Parachain(1002).into().versioned(); + let v2_location = Parachain(1002).into_versioned(); VersionNotifyTargets::::insert(2, v2_location, (71, 0, 1)); // New version. @@ -989,9 +989,9 @@ fn subscription_side_upgrades_work_with_multistage_notify() { assert_eq!( contents, vec![ - (XCM_VERSION, Parachain(1000).into().versioned(), (69, 0, 2)), - (XCM_VERSION, Parachain(1001).into().versioned(), (70, 0, 2)), - (XCM_VERSION, Parachain(1002).into().versioned(), (71, 0, 2)), + (XCM_VERSION, Parachain(1000).into_versioned(), (69, 0, 2)), + (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 2)), + (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 2)), ] ); }); diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 6d757d6de9b1..2f9612e30abc 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -356,9 +356,9 @@ impl From for VersionedMultiAssets { } } -impl From for VersionedMultiAssets { - fn from(x: v3::MultiAssets) -> Self { - VersionedMultiAssets::V3(x) +impl> From for VersionedMultiAssets { + fn from(x: T) -> Self { + VersionedMultiAssets::V3(x.into()) } } @@ -593,6 +593,7 @@ pub trait GetWeight { } #[test] -fn test_build() { - +fn conversion_works() { + use latest::prelude::*; + let _: VersionedMultiAssets = (Here, 1).into(); } \ No newline at end of file diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index 6e1061210fac..b1116073c519 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -509,3 +509,10 @@ impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter { }) } } + +#[test] +fn conversion_works() { + use super::prelude::*; + + let _: MultiAssets = (Here, 1).into(); +} \ No newline at end of file diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 85f5c78396a7..46226bd3125e 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -191,7 +191,7 @@ fn basic_asset_trap_should_work() { add_asset(1001, (Here, 1000)); // They want to transfer 100 of them to their sibling parachain #2 but have a problem let r = XcmExecutor::::execute_xcm( - Parachain(1).into(), + Parachain(1), Xcm(vec![ WithdrawAsset((Here, 100).into()), DepositAsset { @@ -208,7 +208,7 @@ fn basic_asset_trap_should_work() { // Incorrect ticket doesn't work. let old_trapped_assets = TrappedAssets::get(); let r = XcmExecutor::::execute_xcm( - Parachain(1).into(), + Parachain(1), Xcm(vec![ ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(1).into() }, DepositAsset { @@ -226,7 +226,7 @@ fn basic_asset_trap_should_work() { // Incorrect origin doesn't work. let old_trapped_assets = TrappedAssets::get(); let r = XcmExecutor::::execute_xcm( - Parachain(2).into(), + Parachain(2), Xcm(vec![ ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() }, DepositAsset { @@ -244,7 +244,7 @@ fn basic_asset_trap_should_work() { // Incorrect assets doesn't work. let old_trapped_assets = TrappedAssets::get(); let r = XcmExecutor::::execute_xcm( - Parachain(1).into(), + Parachain(1), Xcm(vec![ ClaimAsset { assets: (Here, 101).into(), ticket: GeneralIndex(0).into() }, DepositAsset { @@ -260,7 +260,7 @@ fn basic_asset_trap_should_work() { assert_eq!(old_trapped_assets, TrappedAssets::get()); let r = XcmExecutor::::execute_xcm( - Parachain(1).into(), + Parachain(1), Xcm(vec![ ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() }, DepositAsset { @@ -276,7 +276,7 @@ fn basic_asset_trap_should_work() { // Same again doesn't work :-) let r = XcmExecutor::::execute_xcm( - Parachain(1).into(), + Parachain(1), Xcm(vec![ ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() }, DepositAsset { @@ -316,25 +316,25 @@ fn errors_should_return_unused_weight() { let limit = ::Weigher::weight(&mut message).unwrap(); assert_eq!(limit, 30); - let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); assert_eq!(r, Outcome::Complete(30)); assert_eq!(assets(3), vec![(Here, 7).into()]); assert_eq!(assets(3000), vec![(Here, 4).into()]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); assert_eq!(r, Outcome::Incomplete(30, XcmError::NotWithdrawable)); assert_eq!(assets(3), vec![(Here, 10).into()]); assert_eq!(assets(3000), vec![(Here, 1).into()]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); assert_eq!(r, Outcome::Incomplete(20, XcmError::NotWithdrawable)); assert_eq!(assets(3), vec![(Here, 11).into()]); assert_eq!(assets(3000), vec![]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here.into(), message, limit); + let r = XcmExecutor::::execute_xcm(Here, message, limit); assert_eq!(r, Outcome::Incomplete(10, XcmError::NotWithdrawable)); assert_eq!(assets(3), vec![(Here, 11).into()]); assert_eq!(assets(3000), vec![]); @@ -402,13 +402,13 @@ fn code_registers_should_work() { let limit = ::Weigher::weight(&mut message).unwrap(); assert_eq!(limit, 70); - let r = XcmExecutor::::execute_xcm(Here.into(), message.clone(), limit); + let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); assert_eq!(r, Outcome::Complete(50)); // We don't pay the 20 weight for the error handler. assert_eq!(assets(3), vec![(Here, 13).into()]); assert_eq!(assets(3000), vec![(Here, 8).into()]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here.into(), message, limit); + let r = XcmExecutor::::execute_xcm(Here, message, limit); assert_eq!(r, Outcome::Complete(70)); // We pay the full weight here. assert_eq!(assets(3), vec![(Here, 20).into()]); assert_eq!(assets(3000), vec![(Here, 1).into()]); @@ -457,7 +457,7 @@ fn reserve_transfer_should_work() { fn simple_version_subscriptions_should_work() { AllowSubsFrom::set(vec![Parent.into()]); - let origin = Parachain(1000).into(); + let origin = Parachain(1000); let message = Xcm::(vec![ SetAppendix(Xcm(vec![])), SubscribeVersion { query_id: 42, max_response_weight: 5000 }, @@ -466,7 +466,7 @@ fn simple_version_subscriptions_should_work() { let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); - let origin = Parachain(1000).into(); + let origin = Parachain(1000); let message = Xcm::(vec![SubscribeVersion { query_id: 42, max_response_weight: 5000 }]); let weight_limit = 10; @@ -481,7 +481,7 @@ fn simple_version_subscriptions_should_work() { #[test] fn version_subscription_instruction_should_work() { - let origin = Parachain(1000).into(); + let origin = Parachain(1000); let message = Xcm::(vec![ DescendOrigin(X1(AccountIndex64 { index: 1, network: None })), SubscribeVersion { query_id: 42, max_response_weight: 5000 }, @@ -514,13 +514,13 @@ fn version_subscription_instruction_should_work() { fn simple_version_unsubscriptions_should_work() { AllowSubsFrom::set(vec![Parent.into()]); - let origin = Parachain(1000).into(); + let origin = Parachain(1000); let message = Xcm::(vec![SetAppendix(Xcm(vec![])), UnsubscribeVersion]); let weight_limit = 20; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); - let origin = Parachain(1000).into(); + let origin = Parachain(1000); let message = Xcm::(vec![UnsubscribeVersion]); let weight_limit = 10; let r = XcmExecutor::::execute_xcm(origin, message.clone(), weight_limit); @@ -535,7 +535,7 @@ fn simple_version_unsubscriptions_should_work() { #[test] fn version_unsubscription_instruction_should_work() { - let origin = Parachain(1000).into(); + let origin = Parachain(1000); // Not allowed to do it when origin has been changed. let message = Xcm::(vec![ @@ -609,7 +609,7 @@ fn transacting_should_refund_weight() { #[test] fn paid_transacting_should_refund_payment_for_unused_weight() { - let one: MultiLocation = X1(AccountIndex64 { index: 1, network: None }).into(); + let one: MultiLocation = AccountIndex64 { index: 1, network: None }.into(); AllowPaidFrom::set(vec![one.clone()]); add_asset(1, (Parent, 100)); WeightPrice::set((Parent.into(), 1_000_000_000_000)); @@ -664,19 +664,19 @@ fn fungible_multi_asset(location: MultiLocation, amount: u128) -> MultiAsset { #[test] fn weight_trader_tuple_should_work() { - pub const PARA_1: MultiLocation = X1(Parachain(1)).into(); - pub const PARA_2: MultiLocation = X1(Parachain(2)).into(); + let para_1: MultiLocation = Parachain(1).into(); + let para_2: MultiLocation = Parachain(2).into(); parameter_types! { - pub static HereWeightPrice: (AssetId, u128) = (Here.into().into(), WEIGHT_PER_SECOND.into()); - pub static PARA1WeightPrice: (AssetId, u128) = (PARA_1.into(), WEIGHT_PER_SECOND.into()); + pub static HereWeightPrice: (AssetId, u128) = (Here.into(), WEIGHT_PER_SECOND.into()); + pub static Para1WeightPrice: (AssetId, u128) = (Parachain(1).into(), WEIGHT_PER_SECOND.into()); } type Traders = ( // trader one FixedRateOfFungible, // trader two - FixedRateOfFungible, + FixedRateOfFungible, ); let mut traders = Traders::new(); @@ -691,16 +691,16 @@ fn weight_trader_tuple_should_work() { let mut traders = Traders::new(); // trader one failed; trader two buys weight assert_eq!( - traders.buy_weight(5, fungible_multi_asset(PARA_1, 10).into()), - Ok(fungible_multi_asset(PARA_1, 5).into()), + traders.buy_weight(5, fungible_multi_asset(para_1.clone(), 10).into()), + Ok(fungible_multi_asset(para_1.clone(), 5).into()), ); // trader two refunds - assert_eq!(traders.refund_weight(2), Some(fungible_multi_asset(PARA_1, 2))); + assert_eq!(traders.refund_weight(2), Some(fungible_multi_asset(para_1, 2))); let mut traders = Traders::new(); // all traders fails assert_err!( - traders.buy_weight(5, fungible_multi_asset(PARA_2, 10).into()), + traders.buy_weight(5, fungible_multi_asset(para_2, 10).into()), XcmError::TooExpensive, ); // and no refund diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index c48086f94e16..52db74b0f9f0 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -116,7 +116,7 @@ impl configuration::Config for Runtime { parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::here(); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub Ancestry: InteriorMultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here; pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -151,8 +151,8 @@ pub type Barrier = ( ); parameter_types! { - pub const KusamaForStatemine: (MultiAssetFilter, MultiLocation) = - (MultiAssetFilter::Wild(WildMultiAsset::AllOf { id: Concrete(MultiLocation::here()), fun: WildFungible }), X1(Parachain(1000)).into()); + pub KusamaForStatemine: (MultiAssetFilter, MultiLocation) = + (Wild(AllOf { id: Concrete(Here.into()), fun: WildFungible }), Parachain(1000).into()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 4; } diff --git a/xcm/xcm-builder/tests/scenarios.rs b/xcm/xcm-builder/tests/scenarios.rs index b45b9b96c6cf..35a0676a8205 100644 --- a/xcm/xcm-builder/tests/scenarios.rs +++ b/xcm/xcm-builder/tests/scenarios.rs @@ -47,7 +47,7 @@ fn withdraw_and_deposit_works() { let amount = REGISTER_AMOUNT; let weight = 3 * BaseXcmWeight::get(); let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), @@ -88,7 +88,7 @@ fn report_holding_works() { max_weight: 1_000_000_000, }; let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), @@ -114,7 +114,7 @@ fn report_holding_works() { // now do a successful transfer let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), @@ -169,14 +169,14 @@ fn teleport_to_statemine_works() { buy_execution(), // unchecked mock value DepositAsset { assets: AllCounted(1).into(), - beneficiary: (1, Parachain(PARA_ID)).into(), + beneficiary: (Parent, Parachain(PARA_ID)).into(), }, ]; let weight = 3 * BaseXcmWeight::get(); // teleports are allowed to community chains, even in the absence of trust from their side. let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), @@ -202,7 +202,7 @@ fn teleport_to_statemine_works() { // teleports are allowed from statemine to kusama. let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), @@ -257,12 +257,12 @@ fn reserve_based_transfer_works() { buy_execution(), // unchecked mock value DepositAsset { assets: AllCounted(1).into(), - beneficiary: (1, Parachain(PARA_ID)).into(), + beneficiary: (Parent, Parachain(PARA_ID)).into(), }, ]; let weight = 3 * BaseXcmWeight::get(); let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID).into(), + Parachain(PARA_ID), Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index d7b926dd0156..52baf6a6f8a3 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -206,8 +206,8 @@ mod tests { Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), - Box::new(X1(Parachain(1)).into().into()), - Box::new(X1(AccountId32 { network: None, id: ALICE.into() }).into().into()), + Box::new(Parachain(1).into()), + Box::new(AccountId32 { network: None, id: ALICE.into() }.into()), Box::new((Here, withdraw_amount).into()), 0, )); diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 97e337512940..6630028710a0 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -224,7 +224,7 @@ pub mod mock_msg_queue { let hash = Encode::using_encoded(&xcm, T::Hashing::hash); let (result, event) = match Xcm::::try_from(xcm) { Ok(xcm) => { - let location = (1, Parachain(sender.into())); + let location = (Parent, Parachain(sender.into())); match T::XcmExecutor::execute_xcm(location, xcm, max_weight) { Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 8e4a96a07ef1..046e63727ec6 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -94,29 +94,29 @@ impl configuration::Config for Runtime { } parameter_types! { - pub const KsmLocation: MultiLocation = Here.into(); - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const TokenLocation: MultiLocation = Here.into_location(); + pub RelayNetwork: NetworkId = ByUri((&b"example.com"[..]).to_owned()); pub const AnyNetwork: Option = None; - pub Ancestry: InteriorMultiLocation = Here.into(); + pub Ancestry: InteriorMultiLocation = Here; pub UnitWeightCost: Weight = 1_000; } pub type SovereignAccountOf = - (ChildParachainConvertsVia, AccountId32Aliases); + (ChildParachainConvertsVia, AccountId32Aliases); pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; type LocalOriginConverter = ( SovereignSignedViaLocation, ChildParachainAsNative, - SignedAccountId32AsNative, + SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, ); parameter_types! { pub const BaseXcmWeight: Weight = 1_000; - pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1); + pub KsmPerSecond: (AssetId, u128) = (Concrete(TokenLocation::get()), 1); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -145,7 +145,7 @@ impl Config for XcmConfig { type UniversalAliases = Nothing; } -pub type LocalOriginToLocation = SignedToAccountId32; +pub type LocalOriginToLocation = SignedToAccountId32; impl pallet_xcm::Config for Runtime { type Event = Event; diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 8e4a96a07ef1..66e0bff37b9b 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -94,29 +94,29 @@ impl configuration::Config for Runtime { } parameter_types! { - pub const KsmLocation: MultiLocation = Here.into(); - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const TokenLocation: MultiLocation = Here.into_location(); + pub const ThisNetwork: NetworkId = NetworkId::ByUri(Vec::new()); pub const AnyNetwork: Option = None; - pub Ancestry: InteriorMultiLocation = Here.into(); - pub UnitWeightCost: Weight = 1_000; + pub const Ancestry: InteriorMultiLocation = Here; + pub const UnitWeightCost: Weight = 1_000; } pub type SovereignAccountOf = - (ChildParachainConvertsVia, AccountId32Aliases); + (ChildParachainConvertsVia, AccountId32Aliases); pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; type LocalOriginConverter = ( SovereignSignedViaLocation, ChildParachainAsNative, - SignedAccountId32AsNative, + SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, ); parameter_types! { pub const BaseXcmWeight: Weight = 1_000; - pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1); + pub KsmPerSecond: (AssetId, u128) = (Concrete(TokenLocation::get()), 1); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -145,7 +145,7 @@ impl Config for XcmConfig { type UniversalAliases = Nothing; } -pub type LocalOriginToLocation = SignedToAccountId32; +pub type LocalOriginToLocation = SignedToAccountId32; impl pallet_xcm::Config for Runtime { type Event = Event; From bb939e8b5ef5b497cea7ddefd25fc6338130fbd7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 16:04:02 +0100 Subject: [PATCH 05/57] Formatting --- runtime/kusama/src/lib.rs | 3 +- runtime/rococo/src/lib.rs | 3 +- runtime/test-runtime/src/xcm_config.rs | 6 +- runtime/westend/src/lib.rs | 3 +- .../src/fungible/mock.rs | 5 +- xcm/pallet-xcm/src/mock.rs | 7 ++- xcm/pallet-xcm/src/tests.rs | 27 +++++---- xcm/src/lib.rs | 6 +- xcm/src/v0/junction.rs | 4 +- xcm/src/v1/junction.rs | 9 +-- xcm/src/v1/multiasset.rs | 4 +- xcm/src/v1/multilocation.rs | 10 ++-- xcm/src/v2/mod.rs | 13 +++-- xcm/src/v3/mod.rs | 56 +++++++++++-------- xcm/src/v3/multiasset.rs | 9 +-- xcm/xcm-builder/src/location_conversion.rs | 10 +--- xcm/xcm-builder/src/origin_conversion.rs | 6 +- xcm/xcm-executor/src/config.rs | 4 +- xcm/xcm-executor/src/lib.rs | 2 +- 19 files changed, 103 insertions(+), 84 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 3918502771cd..f77eb9f72341 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1427,7 +1427,8 @@ impl xcm_executor::Config for XcmConfig { type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. - type Trader = UsingComponents>; + type Trader = + UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 8d6e3cb07270..b50a5ccd93af 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -729,7 +729,8 @@ impl xcm_executor::Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = UsingComponents>; + type Trader = + UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 4c1dd26c587f..07bf5be607ea 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -14,7 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use frame_support::{parameter_types, traits::{Everything, Nothing}, weights::Weight}; +use frame_support::{ + parameter_types, + traits::{Everything, Nothing}, + weights::Weight, +}; use xcm::latest::prelude::*; use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, SignedToAccountId32}; use xcm_executor::{ diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 4281555247bb..db4ad8268847 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1055,7 +1055,8 @@ impl xcm_executor::Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = WeightInfoBounds, Call, MaxInstructions>; - type Trader = UsingComponents>; + type Trader = + UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 5b5bef1b686a..624fda85e4b1 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -18,7 +18,10 @@ use crate::{fungible as xcm_balances_benchmark, mock::*}; use frame_benchmarking::BenchmarkError; -use frame_support::{parameter_types, traits::{Everything, Nothing}}; +use frame_support::{ + parameter_types, + traits::{Everything, Nothing}, +}; use sp_core::H256; use sp_runtime::{ testing::Header, diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index f156e5ccb27a..fe9e3b4f829b 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use frame_support::{construct_runtime, parameter_types, weights::Weight}; -use frame_support::traits::{Nothing, Everything}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, Nothing}, + weights::Weight, +}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::origin; use sp_core::H256; diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 32ff19154879..d5ad1a01afa0 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -50,8 +50,13 @@ fn report_outcome_notify_works() { }; let notify = Call::TestNotifier(call); new_test_ext_with_balances(balances).execute_with(|| { - XcmPallet::report_outcome_notify(&mut message, Parachain(PARA_ID).into_location(), notify, 100) - .unwrap(); + XcmPallet::report_outcome_notify( + &mut message, + Parachain(PARA_ID).into_location(), + notify, + 100, + ) + .unwrap(); assert_eq!( message, Xcm(vec![ @@ -152,8 +157,7 @@ fn send_works() { let balances = vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { - let sender: MultiLocation = - AccountId32 { network: None, id: ALICE.into() }.into(); + let sender: MultiLocation = AccountId32 { network: None, id: ALICE.into() }.into(); let message = Xcm(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), ClearOrigin, @@ -340,8 +344,7 @@ fn reserve_transfer_assets_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let weight = BaseXcmWeight::get(); - let dest: MultiLocation = - Junction::AccountId32 { network: None, id: ALICE.into() }.into(); + let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::reserve_transfer_assets( Origin::signed(ALICE), @@ -386,8 +389,7 @@ fn limited_reserve_transfer_assets_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let weight = BaseXcmWeight::get(); - let dest: MultiLocation = - Junction::AccountId32 { network: None, id: ALICE.into() }.into(); + let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( Origin::signed(ALICE), @@ -433,8 +435,7 @@ fn unlimited_reserve_transfer_assets_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let weight = BaseXcmWeight::get(); - let dest: MultiLocation = - Junction::AccountId32 { network: None, id: ALICE.into() }.into(); + let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( Origin::signed(ALICE), @@ -478,8 +479,7 @@ fn execute_withdraw_to_deposit_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let weight = 3 * BaseXcmWeight::get(); - let dest: MultiLocation = - Junction::AccountId32 { network: None, id: BOB.into() }.into(); + let dest: MultiLocation = Junction::AccountId32 { network: None, id: BOB.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::execute( Origin::signed(ALICE), @@ -505,8 +505,7 @@ fn trapped_assets_can_be_claimed() { let balances = vec![(ALICE, INITIAL_BALANCE), (BOB, INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let weight = 6 * BaseXcmWeight::get(); - let dest: MultiLocation = - Junction::AccountId32 { network: None, id: BOB.into() }.into(); + let dest: MultiLocation = Junction::AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::execute( Origin::signed(ALICE), diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 2f9612e30abc..1cb369199a9b 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -78,7 +78,7 @@ pub trait IntoVersion: Sized { pub enum VersionedMultiLocation { V0(v0::MultiLocation), V1(v1::MultiLocation), - V3(v3::MultiLocation) + V3(v3::MultiLocation), } impl IntoVersion for VersionedMultiLocation { @@ -261,7 +261,7 @@ impl IntoVersion for VersionedMultiAsset { fn into_version(self, n: Version) -> Result { Ok(match n { 0 => Self::V0(self.try_into()?), - 1 | 2 => Self::V1(self.try_into()?), + 1 | 2 => Self::V1(self.try_into()?), 3 => Self::V3(self.try_into()?), _ => return Err(()), }) @@ -596,4 +596,4 @@ pub trait GetWeight { fn conversion_works() { use latest::prelude::*; let _: VersionedMultiAssets = (Here, 1).into(); -} \ No newline at end of file +} diff --git a/xcm/src/v0/junction.rs b/xcm/src/v0/junction.rs index 6f192ff4045b..0d871d5179dc 100644 --- a/xcm/src/v0/junction.rs +++ b/xcm/src/v0/junction.rs @@ -16,11 +16,11 @@ //! Support data structures for `MultiLocation`, primarily the `Junction` datatype. -use core::convert::TryInto; +use crate::v3::NetworkId as NewNetworkId; use alloc::vec::Vec; +use core::convert::TryInto; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use crate::v3::NetworkId as NewNetworkId; /// A global identifier of an account-bearing consensus system. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] diff --git a/xcm/src/v1/junction.rs b/xcm/src/v1/junction.rs index 62b5a125f5a2..07d09a0b24e9 100644 --- a/xcm/src/v1/junction.rs +++ b/xcm/src/v1/junction.rs @@ -17,8 +17,7 @@ //! Support data structures for `MultiLocation`, primarily the `Junction` datatype. use super::{BodyId, BodyPart, Junctions, MultiLocation, NetworkId}; -use crate::v0::Junction as OldJunction; -use crate::v3::Junction as NewJunction; +use crate::{v0::Junction as OldJunction, v3::Junction as NewJunction}; use alloc::vec::Vec; use core::convert::{TryFrom, TryInto}; use parity_scale_codec::{self, Decode, Encode}; @@ -88,8 +87,7 @@ impl TryFrom for Junction { Parent => Err(()), Parachain(id) => Ok(Self::Parachain(id)), AccountId32 { network, id } => Ok(Self::AccountId32 { network, id }), - AccountIndex64 { network, index } => - Ok(Self::AccountIndex64 { network, index }), + AccountIndex64 { network, index } => Ok(Self::AccountIndex64 { network, index }), AccountKey20 { network, key } => Ok(Self::AccountKey20 { network, key }), PalletInstance(index) => Ok(Self::PalletInstance(index)), GeneralIndex(id) => Ok(Self::GeneralIndex(id)), @@ -107,8 +105,7 @@ impl TryFrom for Junction { use NewJunction::*; Ok(match value { Parachain(id) => Self::Parachain(id), - AccountId32 { network, id } => - Self::AccountId32 { network: network.try_into()?, id }, + AccountId32 { network, id } => Self::AccountId32 { network: network.try_into()?, id }, AccountIndex64 { network, index } => Self::AccountIndex64 { network: network.try_into()?, index }, AccountKey20 { network, key } => diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index def92a0161ee..87114c49eee6 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -25,8 +25,8 @@ use super::MultiLocation; use crate::v3::{ - AssetId as NewAssetId, MultiAsset as NewMultiAsset, MultiAssets as NewMultiAssets, - MultiAssetFilter as NewMultiAssetFilter, WildMultiAsset as NewWildMultiAsset + AssetId as NewAssetId, MultiAsset as NewMultiAsset, MultiAssetFilter as NewMultiAssetFilter, + MultiAssets as NewMultiAssets, WildMultiAsset as NewWildMultiAsset, }; use alloc::{vec, vec::Vec}; use core::{ diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index 8ce774ff6e55..2ea7efd26aed 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -18,7 +18,10 @@ use super::Junction; use crate::v3::MultiLocation as NewMultiLocation; -use core::{convert::{TryFrom, TryInto}, mem, result}; +use core::{ + convert::{TryFrom, TryInto}, + mem, result, +}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -383,10 +386,7 @@ impl MultiLocation { impl TryFrom for MultiLocation { type Error = (); fn try_from(x: NewMultiLocation) -> result::Result { - Ok(MultiLocation { - parents: x.parents, - interior: x.interior.try_into()?, - }) + Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? }) } } diff --git a/xcm/src/v2/mod.rs b/xcm/src/v2/mod.rs index 5544d591d787..dd42bad38532 100644 --- a/xcm/src/v2/mod.rs +++ b/xcm/src/v2/mod.rs @@ -934,10 +934,15 @@ impl TryFrom> for Instruction { ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), QueryResponse { query_id, response, max_weight } => Self::QueryResponse { query_id, response: response.try_into()?, max_weight }, - TransferAsset { assets, beneficiary } => - Self::TransferAsset { assets: assets.try_into()?, beneficiary: beneficiary.try_into()? }, - TransferReserveAsset { assets, dest, xcm } => - Self::TransferReserveAsset { assets: assets.try_into()?, dest: dest.try_into()?, xcm: xcm.try_into()? }, + TransferAsset { assets, beneficiary } => Self::TransferAsset { + assets: assets.try_into()?, + beneficiary: beneficiary.try_into()?, + }, + TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset { + assets: assets.try_into()?, + dest: dest.try_into()?, + xcm: xcm.try_into()?, + }, HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 6c7026933e81..7d8828b6a240 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -28,24 +28,24 @@ use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; use scale_info::TypeInfo; -mod multiasset; -mod traits; mod junction; pub(crate) mod junctions; +mod multiasset; mod multilocation; +mod traits; +pub use junction::{Junction, NetworkId}; +pub use junctions::Junctions; pub use multiasset::{ AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, WildFungibility, WildMultiAsset, }; -pub use traits::{ - Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo, -}; -pub use junction::{Junction, NetworkId}; -pub use junctions::{Junctions}; pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; +pub use traits::{ + Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo, +}; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -131,8 +131,7 @@ pub mod prelude { InteriorMultiLocation, Junction::{self, *}, Junctions::{self, *}, - MaybeErrorCode, - MultiAsset, + MaybeErrorCode, MultiAsset, MultiAssetFilter::{self, *}, MultiAssets, MultiLocation, NetworkId::{self, *}, @@ -895,9 +894,15 @@ impl TryFrom> for Instruction { ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), QueryResponse { query_id, response, max_weight } => Self::QueryResponse { query_id, response: response.try_into()?, max_weight }, - TransferAsset { assets, beneficiary } => Self::TransferAsset { assets: assets.try_into()?, beneficiary: beneficiary.try_into()? }, - TransferReserveAsset { assets, dest, xcm } => - Self::TransferReserveAsset { assets: assets.try_into()?, dest: dest.try_into()?, xcm: xcm.try_into()? }, + TransferAsset { assets, beneficiary } => Self::TransferAsset { + assets: assets.try_into()?, + beneficiary: beneficiary.try_into()?, + }, + TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset { + assets: assets.try_into()?, + dest: dest.try_into()?, + xcm: xcm.try_into()?, + }, HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, @@ -916,8 +921,10 @@ impl TryFrom> for Instruction { }; Self::ReportError(response_info) }, - DepositAsset { assets, max_assets, beneficiary } => - Self::DepositAsset { assets: (assets, max_assets).try_into()?, beneficiary: beneficiary.try_into()? }, + DepositAsset { assets, max_assets, beneficiary } => Self::DepositAsset { + assets: (assets, max_assets).try_into()?, + beneficiary: beneficiary.try_into()?, + }, DepositReserveAsset { assets, max_assets, dest, xcm } => { let assets = (assets, max_assets).try_into()?; Self::DepositReserveAsset { assets, dest: dest.try_into()?, xcm: xcm.try_into()? } @@ -932,8 +939,11 @@ impl TryFrom> for Instruction { reserve: reserve.try_into()?, xcm: xcm.try_into()?, }, - InitiateTeleport { assets, dest, xcm } => - Self::InitiateTeleport { assets: assets.try_into()?, dest: dest.try_into()?, xcm: xcm.try_into()? }, + InitiateTeleport { assets, dest, xcm } => Self::InitiateTeleport { + assets: assets.try_into()?, + dest: dest.try_into()?, + xcm: xcm.try_into()?, + }, QueryHolding { query_id, dest, assets, max_response_weight } => { let response_info = QueryResponseInfo { destination: dest.try_into()?, @@ -942,9 +952,8 @@ impl TryFrom> for Instruction { }; Self::ReportHolding { response_info, assets: assets.try_into()? } }, - BuyExecution { fees, weight_limit } => { - Self::BuyExecution { fees: fees.try_into()?, weight_limit } - }, + BuyExecution { fees, weight_limit } => + Self::BuyExecution { fees: fees.try_into()?, weight_limit }, ClearOrigin => Self::ClearOrigin, DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), RefundSurplus => Self::RefundSurplus, @@ -968,8 +977,8 @@ impl TryFrom> for Instruction { mod tests { use super::{prelude::*, *}; use crate::v2::{ - MultiAssetFilter as OldMultiAssetFilter, WildMultiAsset as OldWildMultiAsset, - Junctions::Here as OldHere, + Junctions::Here as OldHere, MultiAssetFilter as OldMultiAssetFilter, + WildMultiAsset as OldWildMultiAsset, }; #[test] @@ -1017,7 +1026,10 @@ mod tests { let old_xcm = OldXcm::<()>(vec![ OldInstruction::ReserveAssetDeposited((OldHere, 1).into()), OldInstruction::ClearOrigin, - OldInstruction::BuyExecution { fees: (OldHere, 1).into(), weight_limit: Some(1).into() }, + OldInstruction::BuyExecution { + fees: (OldHere, 1).into(), + weight_limit: Some(1).into(), + }, OldInstruction::DepositAsset { assets: crate::v2::MultiAssetFilter::Wild(crate::v2::WildMultiAsset::All), max_assets: 1, diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index b1116073c519..dedc8698ebd0 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -25,8 +25,8 @@ use super::MultiLocation; use crate::v2::{ - AssetId as OldAssetId, MultiAsset as OldMultiAsset, MultiAssets as OldMultiAssets, - MultiAssetFilter as OldMultiAssetFilter, WildMultiAsset as OldWildMultiAsset, + AssetId as OldAssetId, MultiAsset as OldMultiAsset, MultiAssetFilter as OldMultiAssetFilter, + MultiAssets as OldMultiAssets, WildMultiAsset as OldWildMultiAsset, }; use alloc::{vec, vec::Vec}; use core::{ @@ -503,7 +503,8 @@ impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter { fn try_from(old: (OldMultiAssetFilter, u32)) -> Result { let count = old.1; Ok(match old.0 { - OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 => Self::Definite(x.try_into()?), + OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 => + Self::Definite(x.try_into()?), OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).try_into()?), _ => return Err(()), }) @@ -515,4 +516,4 @@ fn conversion_works() { use super::prelude::*; let _: MultiAssets = (Here, 1).into(); -} \ No newline at end of file +} diff --git a/xcm/xcm-builder/src/location_conversion.rs b/xcm/xcm-builder/src/location_conversion.rs index 4586633b6dd7..66b78370a459 100644 --- a/xcm/xcm-builder/src/location_conversion.rs +++ b/xcm/xcm-builder/src/location_conversion.rs @@ -107,10 +107,7 @@ impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> { fn convert(location: MultiLocation) -> Result { let id = match location { - MultiLocation { - parents: 0, - interior: X1(AccountId32 { id, network: None }), - } => id, + MultiLocation { parents: 0, interior: X1(AccountId32 { id, network: None }) } => id, MultiLocation { parents: 0, interior: X1(AccountId32 { id, network }) } if network == Network::get() => id, @@ -130,10 +127,7 @@ impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> { fn convert(location: MultiLocation) -> Result { let key = match location { - MultiLocation { - parents: 0, - interior: X1(AccountKey20 { key, network: None }), - } => key, + MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network: None }) } => key, MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network }) } if network == Network::get() => key, diff --git a/xcm/xcm-builder/src/origin_conversion.rs b/xcm/xcm-builder/src/origin_conversion.rs index cb1894d0d886..e1b15460df40 100644 --- a/xcm/xcm-builder/src/origin_conversion.rs +++ b/xcm/xcm-builder/src/origin_conversion.rs @@ -196,8 +196,7 @@ where ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::AccountId32 { id, network }) }, - ) if matches!(network, None) || network == Network::get() => - Ok(Origin::signed(id.into())), + ) if matches!(network, None) || network == Network::get() => Ok(Origin::signed(id.into())), (_, origin) => Err(origin), } } @@ -223,8 +222,7 @@ where ( OriginKind::Native, MultiLocation { parents: 0, interior: X1(Junction::AccountKey20 { key, network }) }, - ) if (matches!(network, None) || network == Network::get()) => - Ok(Origin::signed(key.into())), + ) if (matches!(network, None) || network == Network::get()) => Ok(Origin::signed(key.into())), (_, origin) => Err(origin), } } diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index 18a54dd13bdc..1689075e27dc 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -20,10 +20,10 @@ use crate::traits::{ }; use frame_support::{ dispatch::{Dispatchable, Parameter}, - traits::{Get, PalletsInfoAccess, Contains}, + traits::{Contains, Get, PalletsInfoAccess}, weights::{GetDispatchInfo, PostDispatchInfo}, }; -use xcm::latest::{MultiLocation, Junction, SendXcm}; +use xcm::latest::{Junction, MultiLocation, SendXcm}; /// The trait to parameterize the `XcmExecutor`. pub trait Config { diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 101ba34b3c23..9d531abe9886 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -19,7 +19,7 @@ use frame_support::{ dispatch::{Dispatchable, Weight}, ensure, - traits::{Get, PalletsInfoAccess, Contains}, + traits::{Contains, Get, PalletsInfoAccess}, weights::GetDispatchInfo, }; use parity_scale_codec::Encode; From ec2b72d1b8a7451f0598c4ae5cebfa5cf7587760 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 17:27:33 +0100 Subject: [PATCH 06/57] ExportMessage instruction and config type --- xcm/src/v3/mod.rs | 23 +++++++++++++++++++++++ xcm/src/v3/traits.rs | 12 ++++++++++++ xcm/xcm-builder/src/mock.rs | 21 +++++++++++++++++---- xcm/xcm-builder/src/tests.rs | 22 ++++++++++++++++++++++ xcm/xcm-executor/src/config.rs | 5 ++++- xcm/xcm-executor/src/lib.rs | 14 ++++++++++++-- xcm/xcm-executor/src/traits/mod.rs | 2 ++ 7 files changed, 92 insertions(+), 7 deletions(-) diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 7d8828b6a240..9d7cb3a58e01 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -727,6 +727,25 @@ pub enum Instruction { /// /// Errors: *Fallible*. UniversalOrigin(Junction), + + /// Send a message onwards beyond the local consensus system. + /// + /// This will tend to utilize some extra-consensus mechanism, the obvious one being a bridge. + /// + /// - `network`: The remote consensus system to which the message should be exported. + /// - `destination`: The location relative to the remote consensus system to which the message + /// should be sent on arrival. + /// - `xcm`: The message to be exported. + /// + /// As an example, to export a message for execution on Statemine (parachain #1000 in the + /// Kusama network), you would call with `network: NetworkId::Kusama` and + /// `destination: X1(Parachain(1000))`. Alternatively, to export a message for execution on + /// Polkadot, you would call with `network: NetworkId:: Polkadot` and `destination: Here`. + /// + /// Kind: *Instruction* + /// + /// Errors: *Fallible*. + ExportMessage { network: NetworkId, destination: InteriorMultiLocation, xcm: Xcm<()> }, } impl Xcm { @@ -791,6 +810,8 @@ impl Instruction { ReportTransactStatus(repsonse_info) => ReportTransactStatus(repsonse_info), ClearTransactStatus => ClearTransactStatus, UniversalOrigin(j) => UniversalOrigin(j), + ExportMessage { network, destination, xcm } => + ExportMessage { network, destination, xcm }, } } } @@ -845,6 +866,8 @@ impl> GetWeight for Instruction { ReportTransactStatus(response_info) => W::report_transact_status(response_info), ClearTransactStatus => W::clear_transact_status(), UniversalOrigin(j) => W::universal_origin(j), + ExportMessage { network, destination, xcm } => + W::export_message(network, destination, xcm), } } } diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 7b5be8c2f0d4..7eb241c1a025 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -108,6 +108,9 @@ pub enum Error { /// The given operation would lead to an overflow of the Holding Register. #[codec(index = 26)] HoldingWouldOverflow, + /// The message was unable to be exported. + #[codec(index = 27)] + ExportError, // Errors that happen prior to instructions being executed. These fall outside of the XCM spec. /// XCM version not able to be handled. @@ -165,6 +168,7 @@ impl From for Error { SendError::Transport(s) => Error::Transport(s), SendError::DestinationUnsupported => Error::DestinationUnsupported, SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, + SendError::CannotReachNetwork(..) => Error::Unroutable, } } } @@ -274,6 +278,11 @@ pub enum SendError { /// Message could not be sent due to its size exceeding the maximum allowed by the transport /// layer. ExceedsMaxMessageSize, + /// The network was not recognized as being reachable. + /// + /// This is not considered fatal: if there are alternative transport routes available, then + /// they may be attempted. For this reason, the network, destination and message are contained. + CannotReachNetwork(NetworkId, InteriorMultiLocation, Xcm<()>), } /// Result value when attempting to send an XCM message. @@ -442,4 +451,7 @@ pub trait XcmWeightInfo { fn universal_origin(_: &Junction) -> Weight { 0 } + fn export_message(_: &NetworkId, _: &InteriorMultiLocation, _: &Xcm<()>) -> Weight { + 0 + } } diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 783f367c7593..d3175adeb89d 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -36,8 +36,9 @@ pub use sp_std::{ marker::PhantomData, }; pub use xcm::latest::prelude::*; +use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ - traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset}, + traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset, ExportXcm}, Assets, Config, }; @@ -102,17 +103,28 @@ impl GetDispatchInfo for TestCall { thread_local! { pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); + pub static EXPORTED_XCM: RefCell> = RefCell::new(Vec::new()); } pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } -pub struct TestSendXcm; -impl SendXcm for TestSendXcm { +pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm)> { + EXPORTED_XCM.with(|q| (*q.borrow()).clone()) +} +pub struct TestMessageSender; +impl SendXcm for TestMessageSender { fn send_xcm(dest: impl Into, msg: opaque::Xcm) -> SendResult { SENT_XCM.with(|q| q.borrow_mut().push((dest.into(), msg))); Ok(()) } } +pub struct TestMessageExporter; +impl ExportXcm for TestMessageExporter { + fn export_xcm(network: NetworkId, channel: u32, dest: impl Into, msg: opaque::Xcm) -> SendResult { + EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); + Ok(()) + } +} thread_local! { pub static ASSETS: RefCell> = RefCell::new(BTreeMap::new()); @@ -286,7 +298,7 @@ pub type TestBarrier = ( pub struct TestConfig; impl Config for TestConfig { type Call = TestCall; - type XcmSender = TestSendXcm; + type XcmSender = TestMessageSender; type AssetTransactor = TestAssetTransactor; type OriginConverter = TestOriginConverter; type IsReserve = TestIsReserve; @@ -302,4 +314,5 @@ impl Config for TestConfig { type PalletInstancesInfo = TestPalletsInfo; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type UniversalAliases = TestUniversalAliases; + type MessageExporter = TestMessageExporter; } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 46226bd3125e..b3fb0139a76f 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -182,6 +182,28 @@ fn transfer_should_work() { assert_eq!(sent_xcm(), vec![]); } +#[test] +fn export_message_should_work() { + // Bridge chain (assumed to be Relay) lets Parachain #1 have message execution for free. + AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); + // Local parachain #1 issues a transfer asset on Polkadot Relay-chain, transfering 100 Planck to + // Polkadot parachain #2. + let message = Xcm(vec![ + TransferAsset { assets: (Here, 100).into(), beneficiary: Parachain(2).into() }, + ]); + let r = XcmExecutor::::execute_xcm( + Parachain(1), + Xcm(vec![ExportMessage { + network: Polkadot, + destination: Here, + xcm: message.clone(), + }]), + 50, + ); + assert_eq!(r, Outcome::Complete(10)); + assert_eq!(exported_xcm(), vec![(Polkadot, 403611790, Here, message)]); +} + #[test] fn basic_asset_trap_should_work() { // we'll let them have message execution for free. diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index 1689075e27dc..bd4873b720d4 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -16,7 +16,7 @@ use crate::traits::{ ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse, - ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, ExportXcm, }; use frame_support::{ dispatch::{Dispatchable, Parameter}, @@ -82,4 +82,7 @@ pub trait Config { /// The origin locations and specific universal junctions to which they are allowed to elevate /// themselves. type UniversalAliases: Contains<(MultiLocation, Junction)>; + + /// The method of exporting a message. + type MessageExporter: ExportXcm; } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 9d531abe9886..d98ba85ff6b6 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -22,7 +22,8 @@ use frame_support::{ traits::{Contains, Get, PalletsInfoAccess}, weights::GetDispatchInfo, }; -use parity_scale_codec::Encode; +use parity_scale_codec::{Encode, Decode}; +use sp_io::hashing::blake2_128; use sp_runtime::traits::Saturating; use sp_std::{marker::PhantomData, prelude::*}; use xcm::latest::prelude::*; @@ -30,7 +31,7 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse, - ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, ExportXcm, }; mod assets; @@ -577,6 +578,15 @@ impl XcmExecutor { self.origin = Some(new_origin); Ok(()) }, + ExportMessage { network, destination, xcm } => { + // Hash identifies the lane on the exporter which we use. We use the pairwise + // combination of the origin and destination ensure origin/destination pairs will + // generally have their own lanes. + let hash = (&self.origin, &destination).using_encoded(blake2_128); + let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); + Config::MessageExporter::export_xcm(network, channel, destination, xcm)?; + Ok(()) + }, ExchangeAsset { .. } => Err(XcmError::Unimplemented), HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented), HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented), diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index 1312771e719b..8d7ddfef2d6d 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -20,6 +20,8 @@ mod conversion; pub use conversion::{Convert, ConvertOrigin, Decoded, Encoded, Identity, InvertLocation, JustTry}; mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; +mod export; +pub use export::ExportXcm; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; From 6878ed377ff55326255da64fcd8b9f2d1da4ab3d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 17:29:13 +0100 Subject: [PATCH 07/57] Add MessageExporter definitions --- runtime/kusama/src/lib.rs | 1 + runtime/rococo/src/lib.rs | 1 + runtime/test-runtime/src/xcm_config.rs | 1 + runtime/westend/src/lib.rs | 1 + xcm/pallet-xcm-benchmarks/src/fungible/mock.rs | 1 + xcm/pallet-xcm/src/mock.rs | 1 + xcm/xcm-builder/tests/mock/mod.rs | 1 + xcm/xcm-executor/src/config.rs | 6 +++--- xcm/xcm-simulator/example/src/parachain.rs | 1 + xcm/xcm-simulator/example/src/relay_chain.rs | 1 + xcm/xcm-simulator/fuzzer/src/parachain.rs | 1 + xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 1 + 12 files changed, 14 insertions(+), 3 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index f77eb9f72341..987954dc6229 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1435,6 +1435,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index b50a5ccd93af..f3e82eff389e 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -737,6 +737,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 07bf5be607ea..82f5b8841820 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -100,5 +100,6 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = super::Xcm; type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index db4ad8268847..5f7bc394d6a1 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1063,6 +1063,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 624fda85e4b1..37d6bfc8a111 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -148,6 +148,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index fe9e3b4f829b..fe69d1ab01f0 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -272,6 +272,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index 52db74b0f9f0..ee3303df4c7c 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -176,6 +176,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index bd4873b720d4..1c05420c549c 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -79,10 +79,10 @@ pub trait Config { /// and any benchmarks should take that into account. type MaxAssetsIntoHolding: Get; + /// The method of exporting a message. + type MessageExporter: ExportXcm; + /// The origin locations and specific universal junctions to which they are allowed to elevate /// themselves. type UniversalAliases: Contains<(MultiLocation, Junction)>; - - /// The method of exporting a message. - type MessageExporter: ExportXcm; } diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 6630028710a0..1d597e3beae5 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -150,6 +150,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 046e63727ec6..4bdc1cbd17e6 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -142,6 +142,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-simulator/fuzzer/src/parachain.rs b/xcm/xcm-simulator/fuzzer/src/parachain.rs index 025dfb4de864..de1a12df57d7 100644 --- a/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -150,6 +150,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 66e0bff37b9b..d86d8bf1445b 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -142,6 +142,7 @@ impl Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); type UniversalAliases = Nothing; } From 270b6627df12b28c9b4c9b8bd2451180d319c51a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 17:31:30 +0100 Subject: [PATCH 08/57] Formatting --- xcm/xcm-builder/src/mock.rs | 11 +++++++++-- xcm/xcm-builder/src/tests.rs | 11 +++-------- xcm/xcm-executor/src/config.rs | 4 ++-- xcm/xcm-executor/src/lib.rs | 6 +++--- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index d3175adeb89d..ecd9b5178118 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -38,7 +38,9 @@ pub use sp_std::{ pub use xcm::latest::prelude::*; use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ - traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset, ExportXcm}, + traits::{ + ConvertOrigin, ExportXcm, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset, + }, Assets, Config, }; @@ -120,7 +122,12 @@ impl SendXcm for TestMessageSender { } pub struct TestMessageExporter; impl ExportXcm for TestMessageExporter { - fn export_xcm(network: NetworkId, channel: u32, dest: impl Into, msg: opaque::Xcm) -> SendResult { + fn export_xcm( + network: NetworkId, + channel: u32, + dest: impl Into, + msg: opaque::Xcm, + ) -> SendResult { EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); Ok(()) } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index b3fb0139a76f..249948cced6e 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -188,16 +188,11 @@ fn export_message_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); // Local parachain #1 issues a transfer asset on Polkadot Relay-chain, transfering 100 Planck to // Polkadot parachain #2. - let message = Xcm(vec![ - TransferAsset { assets: (Here, 100).into(), beneficiary: Parachain(2).into() }, - ]); + let message = + Xcm(vec![TransferAsset { assets: (Here, 100).into(), beneficiary: Parachain(2).into() }]); let r = XcmExecutor::::execute_xcm( Parachain(1), - Xcm(vec![ExportMessage { - network: Polkadot, - destination: Here, - xcm: message.clone(), - }]), + Xcm(vec![ExportMessage { network: Polkadot, destination: Here, xcm: message.clone() }]), 50, ); assert_eq!(r, Outcome::Complete(10)); diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index 1c05420c549c..bbfb0d808b92 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -15,8 +15,8 @@ // along with Polkadot. If not, see . use crate::traits::{ - ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse, - ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, ExportXcm, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, InvertLocation, + OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ dispatch::{Dispatchable, Parameter}, diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index d98ba85ff6b6..ed3975b83f6a 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -22,7 +22,7 @@ use frame_support::{ traits::{Contains, Get, PalletsInfoAccess}, weights::GetDispatchInfo, }; -use parity_scale_codec::{Encode, Decode}; +use parity_scale_codec::{Decode, Encode}; use sp_io::hashing::blake2_128; use sp_runtime::traits::Saturating; use sp_std::{marker::PhantomData, prelude::*}; @@ -30,8 +30,8 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse, - ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, ExportXcm, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, InvertLocation, + OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; mod assets; From bba850db3a2d954a2c2186ada84bb52fc123f533 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 17:41:34 +0100 Subject: [PATCH 09/57] Missing files --- xcm/procedural/src/v3.rs | 142 ++++++++ xcm/src/v3/junction.rs | 216 +++++++++++ xcm/src/v3/junctions.rs | 491 ++++++++++++++++++++++++++ xcm/src/v3/multilocation.rs | 488 +++++++++++++++++++++++++ xcm/xcm-executor/src/traits/export.rs | 47 +++ 5 files changed, 1384 insertions(+) create mode 100644 xcm/procedural/src/v3.rs create mode 100644 xcm/src/v3/junction.rs create mode 100644 xcm/src/v3/junctions.rs create mode 100644 xcm/src/v3/multilocation.rs create mode 100644 xcm/xcm-executor/src/traits/export.rs diff --git a/xcm/procedural/src/v3.rs b/xcm/procedural/src/v3.rs new file mode 100644 index 000000000000..2c2ab1a19fe5 --- /dev/null +++ b/xcm/procedural/src/v3.rs @@ -0,0 +1,142 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote}; +use syn::{Result, Token}; + +const MAX_JUNCTIONS: usize = 8; + +pub mod multilocation { + use super::*; + + // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. + const MAX_PARENTS: usize = 8; + + pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) + } + + let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS, MAX_PARENTS); + + Ok(quote! { + #from_tuples + }) + } + + fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream { + (0..=max_junctions) + .map(|num_junctions| { + let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::>(); + let idents = + (0..num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); + let interior = if num_junctions == 0 { + quote!(Junctions::Here) + } else { + let variant = format_ident!("X{}", num_junctions); + quote! { + Junctions::#variant( #(#idents .into()),* ) + } + }; + + (0..=max_parents) + .map(|cur_parents| { + let parents = + (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); + let underscores = (0..cur_parents) + .map(|_| Token![_](Span::call_site())) + .collect::>(); + + quote! { + impl< #(#types : Into,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation { + fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self { + Self { parents: #cur_parents as u8, interior: #interior } + } + } + } + }) + .collect::() + }) + .collect() + } +} + +pub mod junctions { + use super::*; + + pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) + } + + // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. + let from_v1 = generate_conversion_from_v1(MAX_JUNCTIONS); + let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS); + + Ok(quote! { + #from_v1 + #from_tuples + }) + } + + fn generate_conversion_from_tuples(max_junctions: usize) -> TokenStream { + (1..=max_junctions) + .map(|num_junctions| { + let idents = + (0..num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); + let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::>(); + let variant = &format_ident!("X{}", num_junctions); + + quote! { + impl<#(#types : Into,)*> From<( #(#types,)* )> for Junctions { + fn from( ( #(#idents,)* ): ( #(#types,)* ) ) -> Self { + Self::#variant( #(#idents .into()),* ) + } + } + } + }) + .collect() + } + + fn generate_conversion_from_v1(max_junctions: usize) -> TokenStream { + let match_variants = (0..max_junctions) + .map(|cur_num| { + let num_ancestors = cur_num + 1; + let variant = format_ident!("X{}", num_ancestors); + let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); + + quote! { + crate::v1::Junctions::#variant( #(#idents),* ) => + #variant( #( core::convert::TryInto::try_into(#idents)? ),* ), + } + }) + .collect::(); + + quote! { + impl core::convert::TryFrom for Junctions { + type Error = (); + fn try_from(mut old: crate::v1::Junctions) -> core::result::Result { + use Junctions::*; + Ok(match old { + crate::v1::Junctions::Here => Here, + #match_variants + }) + } + } + } + } +} diff --git a/xcm/src/v3/junction.rs b/xcm/src/v3/junction.rs new file mode 100644 index 000000000000..f55aecf7f7f7 --- /dev/null +++ b/xcm/src/v3/junction.rs @@ -0,0 +1,216 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Support data structures for `MultiLocation`, primarily the `Junction` datatype. + +use super::{BodyId, BodyPart, Junctions, MultiLocation}; +use crate::{ + v2::{Junction as OldJunction, NetworkId as OldNetworkId}, + VersionedMultiLocation, +}; +use alloc::vec::Vec; +use core::convert::TryFrom; +use parity_scale_codec::{self, Decode, Encode}; +use scale_info::TypeInfo; + +/// A global identifier of a data structure existing within consensus. +/// +/// Maintenance note: Networks with global consensus and which are practically bridgable within the +/// Polkadot ecosystem are given preference over explicit naming in this enumeration. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +pub enum NetworkId { + /// The network identified by a globally recognised URI. Best practice is to use the canonical + /// server name of the primary website providing information on the network. E.g.: + /// + /// - `bitcoin.org` + /// - `ethereum.org` + /// - `polkadot.network` + /// + /// In general (and like the above three networks) networks should either be introduced into + /// this list or referenced by their genesis hash and/or fork properties (in the case that two + /// networks exist with the same genesis hash). + ByUri(Vec), + /// Network specified by the first 32 bytes of its genesis block. + ByGenesis([u8; 32]), + /// Network defined by the first 32-bytes of the hash and number of some block it contains. + ByFork { block_number: u64, block_hash: [u8; 32] }, + /// The Polkadot mainnet Relay-chain. + Polkadot, + /// The Kusama canary-net Relay-chain. + Kusama, + /// The Westend testnet Relay-chain. + Westend, + /// The Rococo testnet Relay-chain. + Rococo, + /// The Wococo testnet Relay-chain. + Wococo, + /// The Ethereum network, including hard-forks supported by the Etheruem Foundation. + EthereumFoundation, + /// The Ethereum network, including hard-forks supported by Ethereum Classic developers. + EthereumClassic, + /// The Bitcoin network, including hard-forks supported by Bitcoin Core development team. + BitcoinCore, + /// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers. + BitcoinCash, +} + +impl From for Option { + fn from(old: OldNetworkId) -> Option { + use OldNetworkId::*; + match old { + Any => None, + Named(name) => Some(NetworkId::ByUri(name)), + Polkadot => Some(NetworkId::Polkadot), + Kusama => Some(NetworkId::Kusama), + } + } +} + +/// A single item in a path to describe the relative location of a consensus system. +/// +/// Each item assumes a pre-existing location as its context and is defined in terms of it. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +pub enum Junction { + /// An indexed parachain belonging to and operated by the context. + /// + /// Generally used when the context is a Polkadot Relay-chain. + Parachain(#[codec(compact)] u32), + /// A 32-byte identifier for an account of a specific network that is respected as a sovereign endpoint within + /// the context. + /// + /// Generally used when the context is a Substrate-based chain. + AccountId32 { network: Option, id: [u8; 32] }, + /// An 8-byte index for an account of a specific network that is respected as a sovereign endpoint within + /// the context. + /// + /// May be used when the context is a Frame-based chain and includes e.g. an indices pallet. + AccountIndex64 { + network: Option, + #[codec(compact)] + index: u64, + }, + /// A 20-byte identifier for an account of a specific network that is respected as a sovereign endpoint within + /// the context. + /// + /// May be used when the context is an Ethereum or Bitcoin chain or smart-contract. + AccountKey20 { network: Option, key: [u8; 20] }, + /// An instanced, indexed pallet that forms a constituent part of the context. + /// + /// Generally used when the context is a Frame-based chain. + PalletInstance(u8), + /// A non-descript index within the context location. + /// + /// Usage will vary widely owing to its generality. + /// + /// NOTE: Try to avoid using this and instead use a more specific item. + GeneralIndex(#[codec(compact)] u128), + /// A nondescript datum acting as a key within the context location. + /// + /// Usage will vary widely owing to its generality. + /// + /// NOTE: Try to avoid using this and instead use a more specific item. + GeneralKey(Vec), + /// The unambiguous child. + /// + /// Not currently used except as a fallback when deriving ancestry. + OnlyChild, + /// A pluralistic body existing within consensus. + /// + /// Typical to be used to represent a governance origin of a chain, but could in principle be used to represent + /// things such as multisigs also. + Plurality { id: BodyId, part: BodyPart }, + /// A global network capable of externalising its own consensus. This is not generally + /// meaningful outside of the universal level. + GlobalConsensus(NetworkId), +} + +impl From for Junction { + fn from(n: NetworkId) -> Self { + Self::GlobalConsensus(n) + } +} + +impl From<[u8; 32]> for Junction { + fn from(id: [u8; 32]) -> Self { + Self::AccountId32 { network: None, id } + } +} + +impl From<[u8; 20]> for Junction { + fn from(key: [u8; 20]) -> Self { + Self::AccountKey20 { network: None, key } + } +} + +impl From for Junction { + fn from(index: u64) -> Self { + Self::AccountIndex64 { network: None, index } + } +} + +impl From for Junction { + fn from(id: u128) -> Self { + Self::GeneralIndex(id) + } +} + +impl From> for Junction { + fn from(id: Vec) -> Self { + Self::GeneralKey(id) + } +} + +impl TryFrom for Junction { + type Error = (); + fn try_from(value: OldJunction) -> Result { + use OldJunction::*; + Ok(match value { + Parachain(id) => Self::Parachain(id), + AccountId32 { network, id } => Self::AccountId32 { network: network.into(), id }, + AccountIndex64 { network, index } => + Self::AccountIndex64 { network: network.into(), index }, + AccountKey20 { network, key } => Self::AccountKey20 { network: network.into(), key }, + PalletInstance(index) => Self::PalletInstance(index), + GeneralIndex(id) => Self::GeneralIndex(id), + GeneralKey(key) => Self::GeneralKey(key), + OnlyChild => Self::OnlyChild, + Plurality { id, part } => Self::Plurality { id: id.into(), part }, + }) + } +} + +impl Junction { + /// Convert `self` into a `MultiLocation` containing 0 parents. + /// + /// Similar to `Into::into`, except that this method can be used in a const evaluation context. + pub const fn into_location(self) -> MultiLocation { + MultiLocation { parents: 0, interior: Junctions::X1(self) } + } + + /// Convert `self` into a `MultiLocation` containing `n` parents. + /// + /// Similar to `Self::into`, with the added ability to specify the number of parent junctions. + pub const fn into_exterior(self, n: u8) -> MultiLocation { + MultiLocation { parents: n, interior: Junctions::X1(self) } + } + + /// Convert `self` into a `MultiLocation` containing 0 parents. + /// + /// Similar to `Into::into`, except that this method can be used in a const evaluation context. + pub const fn into_versioned(self) -> VersionedMultiLocation { + self.into_location().into_versioned() + } +} diff --git a/xcm/src/v3/junctions.rs b/xcm/src/v3/junctions.rs new file mode 100644 index 000000000000..f693e0df368c --- /dev/null +++ b/xcm/src/v3/junctions.rs @@ -0,0 +1,491 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! XCM `Junctions`/`InteriorMultiLocation` datatype. + +use super::{Junction, MultiLocation}; +use core::{convert::TryFrom, mem, result}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +/// Maximum number of `Junction`s that a `Junctions` can contain. +pub(crate) const MAX_JUNCTIONS: usize = 8; + +/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions` +/// implementation uses a Rust `enum` in order to make pattern matching easier. +/// +/// Parent junctions cannot be constructed with this type. Refer to `MultiLocation` for +/// instructions on constructing parent junctions. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +pub enum Junctions { + /// The interpreting consensus system. + Here, + /// A relative path comprising 1 junction. + X1(Junction), + /// A relative path comprising 2 junctions. + X2(Junction, Junction), + /// A relative path comprising 3 junctions. + X3(Junction, Junction, Junction), + /// A relative path comprising 4 junctions. + X4(Junction, Junction, Junction, Junction), + /// A relative path comprising 5 junctions. + X5(Junction, Junction, Junction, Junction, Junction), + /// A relative path comprising 6 junctions. + X6(Junction, Junction, Junction, Junction, Junction, Junction), + /// A relative path comprising 7 junctions. + X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction), + /// A relative path comprising 8 junctions. + X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction), +} + +pub struct JunctionsIterator(Junctions); +impl Iterator for JunctionsIterator { + type Item = Junction; + fn next(&mut self) -> Option { + self.0.take_first() + } +} + +impl DoubleEndedIterator for JunctionsIterator { + fn next_back(&mut self) -> Option { + self.0.take_last() + } +} + +pub struct JunctionsRefIterator<'a> { + junctions: &'a Junctions, + next: usize, + back: usize, +} + +impl<'a> Iterator for JunctionsRefIterator<'a> { + type Item = &'a Junction; + fn next(&mut self) -> Option<&'a Junction> { + if self.next.saturating_add(self.back) >= self.junctions.len() { + return None + } + + let result = self.junctions.at(self.next); + self.next += 1; + result + } +} + +impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> { + fn next_back(&mut self) -> Option<&'a Junction> { + let next_back = self.back.saturating_add(1); + // checked_sub here, because if the result is less than 0, we end iteration + let index = self.junctions.len().checked_sub(next_back)?; + if self.next > index { + return None + } + self.back = next_back; + + self.junctions.at(index) + } +} + +impl<'a> IntoIterator for &'a Junctions { + type Item = &'a Junction; + type IntoIter = JunctionsRefIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + JunctionsRefIterator { junctions: self, next: 0, back: 0 } + } +} + +impl IntoIterator for Junctions { + type Item = Junction; + type IntoIter = JunctionsIterator; + fn into_iter(self) -> Self::IntoIter { + JunctionsIterator(self) + } +} + +impl Junctions { + /// Convert `self` into a `MultiLocation` containing 0 parents. + /// + /// Similar to `Into::into`, except that this method can be used in a const evaluation context. + pub const fn into_location(self) -> MultiLocation { + MultiLocation { parents: 0, interior: self } + } + + /// Convert `self` into a `MultiLocation` containing `n` parents. + /// + /// Similar to `Self::into`, with the added ability to specify the number of parent junctions. + pub const fn into_exterior(self, n: u8) -> MultiLocation { + MultiLocation { parents: n, interior: self } + } + + /// Returns first junction, or `None` if the location is empty. + pub fn first(&self) -> Option<&Junction> { + match &self { + Junctions::Here => None, + Junctions::X1(ref a) => Some(a), + Junctions::X2(ref a, ..) => Some(a), + Junctions::X3(ref a, ..) => Some(a), + Junctions::X4(ref a, ..) => Some(a), + Junctions::X5(ref a, ..) => Some(a), + Junctions::X6(ref a, ..) => Some(a), + Junctions::X7(ref a, ..) => Some(a), + Junctions::X8(ref a, ..) => Some(a), + } + } + + /// Returns last junction, or `None` if the location is empty. + pub fn last(&self) -> Option<&Junction> { + match &self { + Junctions::Here => None, + Junctions::X1(ref a) => Some(a), + Junctions::X2(.., ref a) => Some(a), + Junctions::X3(.., ref a) => Some(a), + Junctions::X4(.., ref a) => Some(a), + Junctions::X5(.., ref a) => Some(a), + Junctions::X6(.., ref a) => Some(a), + Junctions::X7(.., ref a) => Some(a), + Junctions::X8(.., ref a) => Some(a), + } + } + + /// Splits off the first junction, returning the remaining suffix (first item in tuple) and the first element + /// (second item in tuple) or `None` if it was empty. + pub fn split_first(self) -> (Junctions, Option) { + match self { + Junctions::Here => (Junctions::Here, None), + Junctions::X1(a) => (Junctions::Here, Some(a)), + Junctions::X2(a, b) => (Junctions::X1(b), Some(a)), + Junctions::X3(a, b, c) => (Junctions::X2(b, c), Some(a)), + Junctions::X4(a, b, c, d) => (Junctions::X3(b, c, d), Some(a)), + Junctions::X5(a, b, c, d, e) => (Junctions::X4(b, c, d, e), Some(a)), + Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(b, c, d, e, f), Some(a)), + Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(b, c, d, e, f, g), Some(a)), + Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(b, c, d, e, f, g, h), Some(a)), + } + } + + /// Splits off the last junction, returning the remaining prefix (first item in tuple) and the last element + /// (second item in tuple) or `None` if it was empty. + pub fn split_last(self) -> (Junctions, Option) { + match self { + Junctions::Here => (Junctions::Here, None), + Junctions::X1(a) => (Junctions::Here, Some(a)), + Junctions::X2(a, b) => (Junctions::X1(a), Some(b)), + Junctions::X3(a, b, c) => (Junctions::X2(a, b), Some(c)), + Junctions::X4(a, b, c, d) => (Junctions::X3(a, b, c), Some(d)), + Junctions::X5(a, b, c, d, e) => (Junctions::X4(a, b, c, d), Some(e)), + Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(a, b, c, d, e), Some(f)), + Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(a, b, c, d, e, f), Some(g)), + Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(a, b, c, d, e, f, g), Some(h)), + } + } + + /// Removes the first element from `self`, returning it (or `None` if it was empty). + pub fn take_first(&mut self) -> Option { + let mut d = Junctions::Here; + mem::swap(&mut *self, &mut d); + let (tail, head) = d.split_first(); + *self = tail; + head + } + + /// Removes the last element from `self`, returning it (or `None` if it was empty). + pub fn take_last(&mut self) -> Option { + let mut d = Junctions::Here; + mem::swap(&mut *self, &mut d); + let (head, tail) = d.split_last(); + *self = head; + tail + } + + /// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow. + pub fn push(&mut self, new: impl Into) -> result::Result<(), Junction> { + let new = new.into(); + let mut dummy = Junctions::Here; + mem::swap(self, &mut dummy); + match dummy.pushed_with(new) { + Ok(s) => { + *self = s; + Ok(()) + }, + Err((s, j)) => { + *self = s; + Err(j) + }, + } + } + + /// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow. + pub fn push_front(&mut self, new: impl Into) -> result::Result<(), Junction> { + let new = new.into(); + let mut dummy = Junctions::Here; + mem::swap(self, &mut dummy); + match dummy.pushed_front_with(new) { + Ok(s) => { + *self = s; + Ok(()) + }, + Err((s, j)) => { + *self = s; + Err(j) + }, + } + } + + /// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the + /// original value of `self` and `new` in case of overflow. + pub fn pushed_with(self, new: impl Into) -> result::Result { + let new = new.into(); + Ok(match self { + Junctions::Here => Junctions::X1(new), + Junctions::X1(a) => Junctions::X2(a, new), + Junctions::X2(a, b) => Junctions::X3(a, b, new), + Junctions::X3(a, b, c) => Junctions::X4(a, b, c, new), + Junctions::X4(a, b, c, d) => Junctions::X5(a, b, c, d, new), + Junctions::X5(a, b, c, d, e) => Junctions::X6(a, b, c, d, e, new), + Junctions::X6(a, b, c, d, e, f) => Junctions::X7(a, b, c, d, e, f, new), + Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(a, b, c, d, e, f, g, new), + s => Err((s, new))?, + }) + } + + /// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the + /// original value of `self` and `new` in case of overflow. + pub fn pushed_front_with( + self, + new: impl Into, + ) -> result::Result { + let new = new.into(); + Ok(match self { + Junctions::Here => Junctions::X1(new), + Junctions::X1(a) => Junctions::X2(new, a), + Junctions::X2(a, b) => Junctions::X3(new, a, b), + Junctions::X3(a, b, c) => Junctions::X4(new, a, b, c), + Junctions::X4(a, b, c, d) => Junctions::X5(new, a, b, c, d), + Junctions::X5(a, b, c, d, e) => Junctions::X6(new, a, b, c, d, e), + Junctions::X6(a, b, c, d, e, f) => Junctions::X7(new, a, b, c, d, e, f), + Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(new, a, b, c, d, e, f, g), + s => Err((s, new))?, + }) + } + + /// Mutate `self` so that it is suffixed with `suffix`. + /// + /// Does not modify `self` and returns `Err` with `suffix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # fn main() { + /// let mut m = X1(Parachain(21)); + /// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(())); + /// assert_eq!(m, X2(Parachain(21), PalletInstance(3))); + /// # } + /// ``` + pub fn append_with(&mut self, suffix: impl Into) -> Result<(), Junctions> { + let suffix = suffix.into(); + if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS { + return Err(suffix) + } + for j in suffix.into_iter() { + self.push(j).expect("Already checked the sum of the len()s; qed") + } + Ok(()) + } + + /// Returns the number of junctions in `self`. + pub const fn len(&self) -> usize { + match &self { + Junctions::Here => 0, + Junctions::X1(..) => 1, + Junctions::X2(..) => 2, + Junctions::X3(..) => 3, + Junctions::X4(..) => 4, + Junctions::X5(..) => 5, + Junctions::X6(..) => 6, + Junctions::X7(..) => 7, + Junctions::X8(..) => 8, + } + } + + /// Returns the junction at index `i`, or `None` if the location doesn't contain that many elements. + pub fn at(&self, i: usize) -> Option<&Junction> { + Some(match (i, self) { + (0, Junctions::X1(ref a)) => a, + (0, Junctions::X2(ref a, ..)) => a, + (0, Junctions::X3(ref a, ..)) => a, + (0, Junctions::X4(ref a, ..)) => a, + (0, Junctions::X5(ref a, ..)) => a, + (0, Junctions::X6(ref a, ..)) => a, + (0, Junctions::X7(ref a, ..)) => a, + (0, Junctions::X8(ref a, ..)) => a, + (1, Junctions::X2(_, ref a)) => a, + (1, Junctions::X3(_, ref a, ..)) => a, + (1, Junctions::X4(_, ref a, ..)) => a, + (1, Junctions::X5(_, ref a, ..)) => a, + (1, Junctions::X6(_, ref a, ..)) => a, + (1, Junctions::X7(_, ref a, ..)) => a, + (1, Junctions::X8(_, ref a, ..)) => a, + (2, Junctions::X3(_, _, ref a)) => a, + (2, Junctions::X4(_, _, ref a, ..)) => a, + (2, Junctions::X5(_, _, ref a, ..)) => a, + (2, Junctions::X6(_, _, ref a, ..)) => a, + (2, Junctions::X7(_, _, ref a, ..)) => a, + (2, Junctions::X8(_, _, ref a, ..)) => a, + (3, Junctions::X4(_, _, _, ref a)) => a, + (3, Junctions::X5(_, _, _, ref a, ..)) => a, + (3, Junctions::X6(_, _, _, ref a, ..)) => a, + (3, Junctions::X7(_, _, _, ref a, ..)) => a, + (3, Junctions::X8(_, _, _, ref a, ..)) => a, + (4, Junctions::X5(_, _, _, _, ref a)) => a, + (4, Junctions::X6(_, _, _, _, ref a, ..)) => a, + (4, Junctions::X7(_, _, _, _, ref a, ..)) => a, + (4, Junctions::X8(_, _, _, _, ref a, ..)) => a, + (5, Junctions::X6(_, _, _, _, _, ref a)) => a, + (5, Junctions::X7(_, _, _, _, _, ref a, ..)) => a, + (5, Junctions::X8(_, _, _, _, _, ref a, ..)) => a, + (6, Junctions::X7(_, _, _, _, _, _, ref a)) => a, + (6, Junctions::X8(_, _, _, _, _, _, ref a, ..)) => a, + (7, Junctions::X8(_, _, _, _, _, _, _, ref a)) => a, + _ => return None, + }) + } + + /// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't contain that many + /// elements. + pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { + Some(match (i, self) { + (0, Junctions::X1(ref mut a)) => a, + (0, Junctions::X2(ref mut a, ..)) => a, + (0, Junctions::X3(ref mut a, ..)) => a, + (0, Junctions::X4(ref mut a, ..)) => a, + (0, Junctions::X5(ref mut a, ..)) => a, + (0, Junctions::X6(ref mut a, ..)) => a, + (0, Junctions::X7(ref mut a, ..)) => a, + (0, Junctions::X8(ref mut a, ..)) => a, + (1, Junctions::X2(_, ref mut a)) => a, + (1, Junctions::X3(_, ref mut a, ..)) => a, + (1, Junctions::X4(_, ref mut a, ..)) => a, + (1, Junctions::X5(_, ref mut a, ..)) => a, + (1, Junctions::X6(_, ref mut a, ..)) => a, + (1, Junctions::X7(_, ref mut a, ..)) => a, + (1, Junctions::X8(_, ref mut a, ..)) => a, + (2, Junctions::X3(_, _, ref mut a)) => a, + (2, Junctions::X4(_, _, ref mut a, ..)) => a, + (2, Junctions::X5(_, _, ref mut a, ..)) => a, + (2, Junctions::X6(_, _, ref mut a, ..)) => a, + (2, Junctions::X7(_, _, ref mut a, ..)) => a, + (2, Junctions::X8(_, _, ref mut a, ..)) => a, + (3, Junctions::X4(_, _, _, ref mut a)) => a, + (3, Junctions::X5(_, _, _, ref mut a, ..)) => a, + (3, Junctions::X6(_, _, _, ref mut a, ..)) => a, + (3, Junctions::X7(_, _, _, ref mut a, ..)) => a, + (3, Junctions::X8(_, _, _, ref mut a, ..)) => a, + (4, Junctions::X5(_, _, _, _, ref mut a)) => a, + (4, Junctions::X6(_, _, _, _, ref mut a, ..)) => a, + (4, Junctions::X7(_, _, _, _, ref mut a, ..)) => a, + (4, Junctions::X8(_, _, _, _, ref mut a, ..)) => a, + (5, Junctions::X6(_, _, _, _, _, ref mut a)) => a, + (5, Junctions::X7(_, _, _, _, _, ref mut a, ..)) => a, + (5, Junctions::X8(_, _, _, _, _, ref mut a, ..)) => a, + (6, Junctions::X7(_, _, _, _, _, _, ref mut a)) => a, + (6, Junctions::X8(_, _, _, _, _, _, ref mut a, ..)) => a, + (7, Junctions::X8(_, _, _, _, _, _, _, ref mut a)) => a, + _ => return None, + }) + } + + /// Returns a reference iterator over the junctions. + pub fn iter(&self) -> JunctionsRefIterator { + JunctionsRefIterator { junctions: self, next: 0, back: 0 } + } + + /// Returns a reference iterator over the junctions in reverse. + #[deprecated(note = "Please use iter().rev()")] + pub fn iter_rev(&self) -> impl Iterator + '_ { + self.iter().rev() + } + + /// Consumes `self` and returns an iterator over the junctions in reverse. + #[deprecated(note = "Please use into_iter().rev()")] + pub fn into_iter_rev(self) -> impl Iterator { + self.into_iter().rev() + } + + /// Ensures that self begins with `prefix` and that it has a single `Junction` item following. + /// If so, returns a reference to this `Junction` item. + /// + /// # Example + /// ```rust + /// # use xcm::v1::{Junctions::*, Junction::*}; + /// # fn main() { + /// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild); + /// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild)); + /// assert_eq!(m.match_and_split(&X1(Parachain(2))), None); + /// # } + /// ``` + pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> { + if prefix.len() + 1 != self.len() { + return None + } + for i in 0..prefix.len() { + if prefix.at(i) != self.at(i) { + return None + } + } + return self.at(prefix.len()) + } +} + +impl TryFrom for Junctions { + type Error = (); + fn try_from(x: MultiLocation) -> result::Result { + if x.parents > 0 { + Err(()) + } else { + Ok(x.interior) + } + } +} + +impl> From for Junctions { + fn from(x: T) -> Self { + Self::X1(x.into()) + } +} + +impl From<[Junction; 0]> for Junctions { + fn from(_: [Junction; 0]) -> Self { + Self::Here + } +} + +impl From<()> for Junctions { + fn from(_: ()) -> Self { + Self::Here + } +} + +xcm_procedural::impl_conversion_functions_for_junctions_v3!(); + +#[test] +fn test_conversion() { + use super::{Junction::*, Junctions::*, NetworkId::*}; + let x: Junctions = GlobalConsensus(Polkadot).into(); + assert_eq!(x, X1(GlobalConsensus(Polkadot))); + let x: Junctions = Polkadot.into(); + assert_eq!(x, X1(GlobalConsensus(Polkadot))); + let x: Junctions = (Polkadot, Kusama).into(); + assert_eq!(x, X2(GlobalConsensus(Polkadot), GlobalConsensus(Kusama))); +} diff --git a/xcm/src/v3/multilocation.rs b/xcm/src/v3/multilocation.rs new file mode 100644 index 000000000000..a957a99635ac --- /dev/null +++ b/xcm/src/v3/multilocation.rs @@ -0,0 +1,488 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! XCM `MultiLocation` datatype. + +use super::{Junction, Junctions}; +use crate::{v2::MultiLocation as OldMultiLocation, VersionedMultiLocation}; +use core::{ + convert::{TryFrom, TryInto}, + result, +}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +/// A relative path between state-bearing consensus systems. +/// +/// A location in a consensus system is defined as an *isolatable state machine* held within global +/// consensus. The location in question need not have a sophisticated consensus algorithm of its +/// own; a single account within Ethereum, for example, could be considered a location. +/// +/// A very-much non-exhaustive list of types of location include: +/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain. +/// - A layer-0 super-chain, e.g. the Polkadot Relay chain. +/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum. +/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based +/// Substrate chain. +/// - An account. +/// +/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the +/// relative path between two locations, and cannot generally be used to refer to a location +/// universally. It is comprised of an integer number of parents specifying the number of times to +/// "escape" upwards into the containing consensus system and then a number of *junctions*, each +/// diving down and specifying some interior portion of state (which may be considered a +/// "sub-consensus" system). +/// +/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum` +/// in order to make pattern matching easier. There are occasions where it is important to ensure +/// that a value is strictly an interior location, in those cases, `Junctions` may be used. +/// +/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system. +#[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo)] +pub struct MultiLocation { + /// The number of parent junctions at the beginning of this `MultiLocation`. + pub parents: u8, + /// The interior (i.e. non-parent) junctions that this `MultiLocation` contains. + pub interior: Junctions, +} + +impl Default for MultiLocation { + fn default() -> Self { + Self { parents: 0, interior: Junctions::Here } + } +} + +/// A relative location which is constrained to be an interior location of the context. +/// +/// See also `MultiLocation`. +pub type InteriorMultiLocation = Junctions; + +impl MultiLocation { + /// Creates a new `MultiLocation` with the given number of parents and interior junctions. + pub fn new(parents: u8, interior: impl Into) -> MultiLocation { + MultiLocation { parents, interior: interior.into() } + } + + /// Consume `self` and return the equivalent `VersionedMultiLocation` value. + pub const fn into_versioned(self) -> VersionedMultiLocation { + VersionedMultiLocation::V3(self) + } + + /// Creates a new `MultiLocation` with 0 parents and a `Here` interior. + /// + /// The resulting `MultiLocation` can be interpreted as the "current consensus system". + pub const fn here() -> MultiLocation { + MultiLocation { parents: 0, interior: Junctions::Here } + } + + /// Creates a new `MultiLocation` which evaluates to the parent context. + pub const fn parent() -> MultiLocation { + MultiLocation { parents: 1, interior: Junctions::Here } + } + + /// Creates a new `MultiLocation` which evaluates to the grand parent context. + pub const fn grandparent() -> MultiLocation { + MultiLocation { parents: 2, interior: Junctions::Here } + } + + /// Creates a new `MultiLocation` with `parents` and an empty (`Here`) interior. + pub const fn ancestor(parents: u8) -> MultiLocation { + MultiLocation { parents, interior: Junctions::Here } + } + + /// Whether the `MultiLocation` has no parents and has a `Here` interior. + pub const fn is_here(&self) -> bool { + self.parents == 0 && self.interior.len() == 0 + } + + /// Return a reference to the interior field. + pub fn interior(&self) -> &Junctions { + &self.interior + } + + /// Return a mutable reference to the interior field. + pub fn interior_mut(&mut self) -> &mut Junctions { + &mut self.interior + } + + /// Returns the number of `Parent` junctions at the beginning of `self`. + pub const fn parent_count(&self) -> u8 { + self.parents + } + + /// Returns boolean indicating whether `self` contains only the specified amount of + /// parents and no interior junctions. + pub const fn contains_parents_only(&self, count: u8) -> bool { + matches!(self.interior, Junctions::Here) && self.parents == count + } + + /// Returns the number of parents and junctions in `self`. + pub const fn len(&self) -> usize { + self.parent_count() as usize + self.interior.len() + } + + /// Returns the first interior junction, or `None` if the location is empty or contains only + /// parents. + pub fn first_interior(&self) -> Option<&Junction> { + self.interior.first() + } + + /// Returns last junction, or `None` if the location is empty or contains only parents. + pub fn last(&self) -> Option<&Junction> { + self.interior.last() + } + + /// Splits off the first interior junction, returning the remaining suffix (first item in tuple) + /// and the first element (second item in tuple) or `None` if it was empty. + pub fn split_first_interior(self) -> (MultiLocation, Option) { + let MultiLocation { parents, interior: junctions } = self; + let (suffix, first) = junctions.split_first(); + let multilocation = MultiLocation { parents, interior: suffix }; + (multilocation, first) + } + + /// Splits off the last interior junction, returning the remaining prefix (first item in tuple) + /// and the last element (second item in tuple) or `None` if it was empty or if `self` only + /// contains parents. + pub fn split_last_interior(self) -> (MultiLocation, Option) { + let MultiLocation { parents, interior: junctions } = self; + let (prefix, last) = junctions.split_last(); + let multilocation = MultiLocation { parents, interior: prefix }; + (multilocation, last) + } + + /// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in + /// case of overflow. + pub fn push_interior(&mut self, new: impl Into) -> result::Result<(), Junction> { + self.interior.push(new) + } + + /// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in + /// case of overflow. + pub fn push_front_interior( + &mut self, + new: impl Into, + ) -> result::Result<(), Junction> { + self.interior.push_front(new) + } + + /// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with theoriginal value of + /// `self` in case of overflow. + pub fn pushed_with_interior( + self, + new: impl Into, + ) -> result::Result { + match self.interior.pushed_with(new) { + Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }), + Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)), + } + } + + /// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the original value of + /// `self` in case of overflow. + pub fn pushed_front_with_interior( + self, + new: impl Into, + ) -> result::Result { + match self.interior.pushed_front_with(new) { + Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }), + Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)), + } + } + + /// Returns the junction at index `i`, or `None` if the location is a parent or if the location + /// does not contain that many elements. + pub fn at(&self, i: usize) -> Option<&Junction> { + let num_parents = self.parents as usize; + if i < num_parents { + return None + } + self.interior.at(i - num_parents) + } + + /// Returns a mutable reference to the junction at index `i`, or `None` if the location is a + /// parent or if it doesn't contain that many elements. + pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { + let num_parents = self.parents as usize; + if i < num_parents { + return None + } + self.interior.at_mut(i - num_parents) + } + + /// Decrements the parent count by 1. + pub fn dec_parent(&mut self) { + self.parents = self.parents.saturating_sub(1); + } + + /// Removes the first interior junction from `self`, returning it + /// (or `None` if it was empty or if `self` contains only parents). + pub fn take_first_interior(&mut self) -> Option { + self.interior.take_first() + } + + /// Removes the last element from `interior`, returning it (or `None` if it was empty or if + /// `self` only contains parents). + pub fn take_last(&mut self) -> Option { + self.interior.take_last() + } + + /// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with + /// the junctions of `prefix` and that it has a single `Junction` item following. + /// If so, returns a reference to this `Junction` item. + /// + /// # Example + /// ```rust + /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # fn main() { + /// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild)); + /// assert_eq!( + /// m.match_and_split(&MultiLocation::new(1, X1(PalletInstance(3)))), + /// Some(&OnlyChild), + /// ); + /// assert_eq!(m.match_and_split(&MultiLocation::new(1, Here)), None); + /// # } + /// ``` + pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> { + if self.parents != prefix.parents { + return None + } + self.interior.match_and_split(&prefix.interior) + } + + /// Mutate `self` so that it is suffixed with `suffix`. + /// + /// Does not modify `self` and returns `Err` with `suffix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # fn main() { + /// let mut m = MultiLocation::new(1, X1(Parachain(21))); + /// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(())); + /// assert_eq!(m, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3)))); + /// # } + /// ``` + pub fn append_with(&mut self, suffix: impl Into) -> Result<(), Junctions> { + self.interior.append_with(suffix.into()) + } + + /// Mutate `self` so that it is prefixed with `prefix`. + /// + /// Does not modify `self` and returns `Err` with `prefix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # fn main() { + /// let mut m = MultiLocation::new(2, X1(PalletInstance(3))); + /// assert_eq!(m.prepend_with(MultiLocation::new(1, X2(Parachain(21), OnlyChild))), Ok(())); + /// assert_eq!(m, MultiLocation::new(1, X1(PalletInstance(3)))); + /// # } + /// ``` + pub fn prepend_with(&mut self, prefix: impl Into) -> Result<(), MultiLocation> { + // prefix self (suffix) + // P .. P I .. I p .. p i .. i + let mut prefix = prefix.into(); + let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize); + let final_interior = self.interior.len().saturating_add(prepend_interior); + if final_interior > super::junctions::MAX_JUNCTIONS { + return Err(prefix) + } + let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len()); + let final_parents = (prefix.parents as usize).saturating_add(suffix_parents); + if final_parents > 255 { + return Err(prefix) + } + + // cancel out the final item on the prefix interior for one of the suffix's parents. + while self.parents > 0 && prefix.take_last().is_some() { + self.dec_parent(); + } + + // now we have either removed all suffix's parents or prefix interior. + // this means we can combine the prefix's and suffix's remaining parents/interior since + // we know that with at least one empty, the overall order will be respected: + // prefix self (suffix) + // P .. P (I) p .. p i .. i => P + p .. (no I) i + // -- or -- + // P .. P I .. I (p) i .. i => P (no p) .. I + i + + self.parents = self.parents.saturating_add(prefix.parents); + for j in prefix.interior.into_iter().rev() { + self.push_front_interior(j) + .expect("final_interior no greater than MAX_JUNCTIONS; qed"); + } + Ok(()) + } +} + +impl TryFrom for MultiLocation { + type Error = (); + fn try_from(x: OldMultiLocation) -> result::Result { + Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? }) + } +} + +/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Parent; +impl From for MultiLocation { + fn from(_: Parent) -> Self { + MultiLocation { parents: 1, interior: Junctions::Here } + } +} + +/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner interior. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct ParentThen(pub Junctions); +impl From for MultiLocation { + fn from(ParentThen(interior): ParentThen) -> Self { + MultiLocation { parents: 1, interior } + } +} + +/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Ancestor(pub u8); +impl From for MultiLocation { + fn from(Ancestor(parents): Ancestor) -> Self { + MultiLocation { parents, interior: Junctions::Here } + } +} + +/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the inner interior. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct AncestorThen(pub u8, pub Interior); +impl> From> for MultiLocation { + fn from(AncestorThen(parents, interior): AncestorThen) -> Self { + MultiLocation { parents, interior: interior.into() } + } +} + +impl From for MultiLocation { + fn from(t: Junctions) -> Self { + Self { parents: 0, interior: t } + } +} + +impl From for MultiLocation { + fn from(t: Junction) -> Self { + Self { parents: 0, interior: Junctions::X1(t) } + } +} + +xcm_procedural::impl_conversion_functions_for_multilocation_v3!(); + +#[cfg(test)] +mod tests { + use crate::v3::prelude::*; + use parity_scale_codec::{Decode, Encode}; + + #[test] + fn conversion_works() { + let x: MultiLocation = Parent.into(); + assert_eq!(x, MultiLocation { parents: 1, interior: Here }); + let x: MultiLocation = (Parent,).into(); + assert_eq!(x, MultiLocation { parents: 1, interior: Here }); + let x: MultiLocation = (Parent, Parent).into(); + assert_eq!(x, MultiLocation { parents: 2, interior: Here }); + let x: MultiLocation = (Parent, Parent, OnlyChild).into(); + assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() }); + let x: MultiLocation = OnlyChild.into(); + assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() }); + let x: MultiLocation = (OnlyChild,).into(); + assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() }); + } + + #[test] + fn encode_and_decode_works() { + let m: MultiLocation = (Parent, Parachain(42), 23u64).into(); + let encoded = m.encode(); + assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec()); + let decoded = MultiLocation::decode(&mut &encoded[..]); + assert_eq!(decoded, Ok(m)); + } + + #[test] + fn match_and_split_works() { + let m: MultiLocation = (Parent, Parachain(42), 23u64).into(); + assert_eq!(m.match_and_split(&Parent.into()), None); + assert_eq!(m.match_and_split(&(Parent, Parachain(42)).into()), Some(&23u64.into())); + assert_eq!(m.match_and_split(&m), None); + } + + #[test] + fn append_with_works() { + let acc: Junction = 23u64.into(); + let mut m: MultiLocation = (Parent, Parachain(42)).into(); + assert_eq!(m.append_with((PalletInstance(3), acc.clone())), Ok(())); + assert_eq!(m, MultiLocation::from((Parent, Parachain(42), PalletInstance(3), acc.clone()))); + + // cannot append to create overly long multilocation + let acc: Junction = 23u64.into(); + let m = MultiLocation { + parents: 254, + interior: (Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild).into(), + }; + let suffix = (PalletInstance(3), acc.clone(), OnlyChild, OnlyChild); + assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix.into())); + } + + #[test] + fn prepend_with_works() { + let mut m: MultiLocation = (Parent, Parachain(42), 23u64).into(); + assert_eq!(m.prepend_with((Parent, OnlyChild)), Ok(())); + assert_eq!(m, MultiLocation::from((Parent, Parachain(42), 23u64))); + + // cannot prepend to create overly long multilocation + let mut m = MultiLocation { parents: 254, interior: X1(Parachain(42)) }; + assert_eq!(m.prepend_with((Parent, Parent)), Err((Parent, Parent).into())); + assert_eq!(m.prepend_with(Parent), Ok(())); + assert_eq!(m, MultiLocation { parents: 255, interior: X1(Parachain(42)) }); + } + + #[test] + fn double_ended_ref_iteration_works() { + let m: Junctions = (Parachain(1000), Parachain(3), PalletInstance(5)).into(); + let mut iter = m.iter(); + + let first = iter.next().unwrap(); + assert_eq!(first, &Parachain(1000)); + let third = iter.next_back().unwrap(); + assert_eq!(third, &PalletInstance(5)); + let second = iter.next_back().unwrap(); + assert_eq!(iter.next(), None); + assert_eq!(iter.next_back(), None); + assert_eq!(second, &Parachain(3)); + + let res = Here + .pushed_with(first.clone()) + .unwrap() + .pushed_with(second.clone()) + .unwrap() + .pushed_with(third.clone()) + .unwrap(); + assert_eq!(m, res); + + // make sure there's no funny business with the 0 indexing + let m = Here; + let mut iter = m.iter(); + + assert_eq!(iter.next(), None); + assert_eq!(iter.next_back(), None); + } +} diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs new file mode 100644 index 000000000000..2adb46d9d93e --- /dev/null +++ b/xcm/xcm-executor/src/traits/export.rs @@ -0,0 +1,47 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use xcm::latest::prelude::*; + +/// Type which is able to send a given message over to another consensus network. +pub trait ExportXcm { + fn export_xcm( + network: NetworkId, + channel: u32, + destination: impl Into, + message: Xcm<()>, + ) -> Result<(), SendError>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl ExportXcm for Tuple { + fn export_xcm( + network: NetworkId, + channel: u32, + dest: impl Into, + message: Xcm<()>, + ) -> SendResult { + let dest = dest.into(); + for_tuples!( #( + // we shadow `dest` and `message` in each expansion for the next one. + let (network, dest, message) = match Tuple::export_xcm(network, channel, dest, message) { + Err(SendError::CannotReachNetwork(n, d, m)) => (n, d, m), + o @ _ => return o, + }; + )* ); + Err(SendError::CannotReachNetwork(network, dest, message)) + } +} From 73e47585f954ecab18b7307f42b9e70ef2308259 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 18:13:50 +0100 Subject: [PATCH 10/57] Fixes --- scripts/gitlab/lingua.dic | 1 + xcm/src/v3/junction.rs | 6 +++--- xcm/src/v3/junctions.rs | 4 ++-- xcm/src/v3/mod.rs | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/gitlab/lingua.dic b/scripts/gitlab/lingua.dic index bf70b0512e40..0e5a0868167d 100644 --- a/scripts/gitlab/lingua.dic +++ b/scripts/gitlab/lingua.dic @@ -247,6 +247,7 @@ SS58 SSL startup/MS stateful +Statemine str struct/MS subcommand/SM diff --git a/xcm/src/v3/junction.rs b/xcm/src/v3/junction.rs index f55aecf7f7f7..724eb99d7ed9 100644 --- a/xcm/src/v3/junction.rs +++ b/xcm/src/v3/junction.rs @@ -28,11 +28,11 @@ use scale_info::TypeInfo; /// A global identifier of a data structure existing within consensus. /// -/// Maintenance note: Networks with global consensus and which are practically bridgable within the +/// Maintenance note: Networks with global consensus and which are practically bridgeable within the /// Polkadot ecosystem are given preference over explicit naming in this enumeration. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] pub enum NetworkId { - /// The network identified by a globally recognised URI. Best practice is to use the canonical + /// The network identified by a globally recognized URI. Best practice is to use the canonical /// server name of the primary website providing information on the network. E.g.: /// /// - `bitcoin.org` @@ -132,7 +132,7 @@ pub enum Junction { /// Typical to be used to represent a governance origin of a chain, but could in principle be used to represent /// things such as multisigs also. Plurality { id: BodyId, part: BodyPart }, - /// A global network capable of externalising its own consensus. This is not generally + /// A global network capable of externalizing its own consensus. This is not generally /// meaningful outside of the universal level. GlobalConsensus(NetworkId), } diff --git a/xcm/src/v3/junctions.rs b/xcm/src/v3/junctions.rs index f693e0df368c..94c7141d8366 100644 --- a/xcm/src/v3/junctions.rs +++ b/xcm/src/v3/junctions.rs @@ -286,7 +286,7 @@ impl Junctions { /// /// # Example /// ```rust - /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation}; /// # fn main() { /// let mut m = X1(Parachain(21)); /// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(())); @@ -428,7 +428,7 @@ impl Junctions { /// /// # Example /// ```rust - /// # use xcm::v1::{Junctions::*, Junction::*}; + /// # use xcm::v3::{Junctions::*, Junction::*}; /// # fn main() { /// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild); /// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild)); diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 9d7cb3a58e01..d576f6b71110 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -719,7 +719,7 @@ pub enum Instruction { /// Set the Origin Register to be some child of the Universal Ancestor. /// /// Safety: Should only be usable if the Origin is trusted to represent the Universal Ancestor - /// child in general. In general, no Origin should be able to represent the Universal Ancester + /// child in general. In general, no Origin should be able to represent the Universal Ancestor /// child which is the root of the local consensus system since it would by extension /// allow it to act as any location within the local consensus. /// From 4bef6070d59f803bf136af180f85e873a28bf7f7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 31 Oct 2021 19:31:15 +0100 Subject: [PATCH 11/57] Initial bridging config API --- xcm/src/v3/junctions.rs | 6 ++-- xcm/src/v3/multilocation.rs | 64 +++++++++++++++++++++++++++++++------ xcm/xcm-builder/src/lib.rs | 63 ++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/xcm/src/v3/junctions.rs b/xcm/src/v3/junctions.rs index 94c7141d8366..2302f404acd9 100644 --- a/xcm/src/v3/junctions.rs +++ b/xcm/src/v3/junctions.rs @@ -449,10 +449,10 @@ impl Junctions { } impl TryFrom for Junctions { - type Error = (); - fn try_from(x: MultiLocation) -> result::Result { + type Error = MultiLocation; + fn try_from(x: MultiLocation) -> result::Result { if x.parents > 0 { - Err(()) + Err(x) } else { Ok(x.interior) } diff --git a/xcm/src/v3/multilocation.rs b/xcm/src/v3/multilocation.rs index a957a99635ac..42ff49d8f309 100644 --- a/xcm/src/v3/multilocation.rs +++ b/xcm/src/v3/multilocation.rs @@ -246,7 +246,7 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation}; /// # fn main() { /// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild)); /// assert_eq!( @@ -269,15 +269,39 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent}; /// # fn main() { - /// let mut m = MultiLocation::new(1, X1(Parachain(21))); - /// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(())); + /// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into(); + /// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(())); /// assert_eq!(m, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3)))); /// # } /// ``` - pub fn append_with(&mut self, suffix: impl Into) -> Result<(), Junctions> { - self.interior.append_with(suffix.into()) + pub fn append_with(&mut self, suffix: impl Into) -> Result<(), Self> { + let prefix = core::mem::replace(self, suffix.into()); + match self.prepend_with(prefix) { + Ok(()) => Ok(()), + Err(prefix) => Err(core::mem::replace(self, prefix)), + } + } + + /// Consume `self` and return its value suffixed with `suffix`. + /// + /// Returns `Err` with the original value of `self` and `suffix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent}; + /// # fn main() { + /// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into(); + /// let r = m.appended_with((Parent, PalletInstance(3))).unwrap(); + /// assert_eq!(r, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3)))); + /// # } + /// ``` + pub fn appended_with(mut self, suffix: impl Into) -> Result { + match self.append_with(suffix) { + Ok(()) => Ok(self), + Err(suffix) => Err((self, suffix)), + } } /// Mutate `self` so that it is prefixed with `prefix`. @@ -286,14 +310,14 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation}; + /// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent}; /// # fn main() { - /// let mut m = MultiLocation::new(2, X1(PalletInstance(3))); - /// assert_eq!(m.prepend_with(MultiLocation::new(1, X2(Parachain(21), OnlyChild))), Ok(())); + /// let mut m: MultiLocation = (Parent, Parent, PalletInstance(3)).into(); + /// assert_eq!(m.prepend_with((Parent, Parachain(21), OnlyChild)), Ok(())); /// assert_eq!(m, MultiLocation::new(1, X1(PalletInstance(3)))); /// # } /// ``` - pub fn prepend_with(&mut self, prefix: impl Into) -> Result<(), MultiLocation> { + pub fn prepend_with(&mut self, prefix: impl Into) -> Result<(), Self> { // prefix self (suffix) // P .. P I .. I p .. p i .. i let mut prefix = prefix.into(); @@ -328,6 +352,26 @@ impl MultiLocation { } Ok(()) } + + /// Consume `self` and return its value prefixed with `prefix`. + /// + /// Returns `Err` with the original value of `self` and `prefix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent}; + /// # fn main() { + /// let m: MultiLocation = (Parent, Parent, PalletInstance(3)).into(); + /// let r = m.prepended_with((Parent, Parachain(21), OnlyChild)).unwrap(); + /// assert_eq!(r, MultiLocation::new(1, X1(PalletInstance(3)))); + /// # } + /// ``` + pub fn prepended_with(mut self, prefix: impl Into) -> Result { + match self.prepend_with(prefix) { + Ok(()) => Ok(self), + Err(prefix) => Err((self, prefix)), + } + } } impl TryFrom for MultiLocation { diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 7d94312a463d..41b081a51d15 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -69,3 +69,66 @@ pub use matches_fungible::{IsAbstract, IsConcrete}; mod filter_asset_location; pub use filter_asset_location::{Case, NativeAsset}; + +mod universal_exports { + use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; + use frame_support::traits::Get; + use xcm::prelude::*; + use xcm_executor::traits::ExportXcm; + + fn ensure_is_remote( + ancestry: impl Into, + dest: impl Into, + ) -> Result<(NetworkId, InteriorMultiLocation), MultiLocation> { + let dest = dest.into(); + let ancestry = ancestry.into(); + let local_network = match ancestry.first() { + Some(GlobalConsensus(network)) => network.clone(), + _ => return Err(dest), + }; + let universal_destination: InteriorMultiLocation = ancestry + .into_location() + .appended_with(dest.clone()) + .map_err(|x| x.1)? + .try_into()?; + let (remote_dest, remote_net) = match universal_destination.split_first() { + (d, Some(GlobalConsensus(n))) if n != local_network => (d, n), + _ => return Err(dest), + }; + Ok((remote_net, remote_dest)) + } + + pub struct LocalExporter(PhantomData<(Exporter, Ancestry)>); + impl> SendXcm for LocalExporter { + fn send_xcm(dest: impl Into, message: Xcm<()>) -> SendResult { + let (network, destination) = match ensure_is_remote(Ancestry::get(), dest) { + Ok(x) => x, + Err(dest) => return Err(SendError::CannotReachDestination(dest, message)), + }; + Exporter::export_xcm(network, 0, destination, message) + } + } + + #[test] + fn local_exporter_works() { + // A Kusama parachain is remote from the Polkadot Relay. + let x = ensure_is_remote(Polkadot, (Parent, Kusama, Parachain(1000))); + assert_eq!(x, Ok((Kusama, Parachain(1000).into()))); + + // Polkadot Relay is remote from a Kusama parachain. + let x = ensure_is_remote((Kusama, Parachain(1000)), (Parent, Parent, Polkadot)); + assert_eq!(x, Ok((Polkadot, ().into()))); + + // Our own parachain is local. + let x = ensure_is_remote(Polkadot, Parachain(1000)); + assert_eq!(x, Err(Parachain(1000).into())); + + // Polkadot's parachain is not remote if we are Polkadot. + let x = ensure_is_remote(Polkadot, (Parent, Polkadot, Parachain(1000))); + assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); + + // If we don't have a consensus ancestor, then we cannot determine remoteness. + let x = ensure_is_remote((), (Parent, Polkadot, Parachain(1000))); + assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); + } +} From c0edea04c31ce59f10b480abb78abb706cee7289 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 2 Nov 2021 13:20:06 +0100 Subject: [PATCH 12/57] Allow for two-stage XCM execution --- xcm/src/v3/mod.rs | 35 ++++++++++++- xcm/src/v3/traits.rs | 44 +++++++++++++---- xcm/xcm-builder/src/lib.rs | 98 ++++++++++++++++++++++++++++--------- xcm/xcm-executor/src/lib.rs | 54 ++++++++------------ 4 files changed, 164 insertions(+), 67 deletions(-) diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index d576f6b71110..56269be4aef8 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -45,6 +45,7 @@ pub use multilocation::{ }; pub use traits::{ Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo, + PreparedMessage, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -78,6 +79,24 @@ impl Xcm { self.0.len() } + /// Return a reference to the inner value. + pub fn inner(&self) -> &Vec> { &self.0 } + + /// Return a mutable reference to the inner value. + pub fn inner_mut(&mut self) -> &mut Vec> { &mut self.0 } + + /// Consume and return the inner value. + pub fn into_inner(self) -> Vec> { self.0 } + + /// Return an iterator over references to the items. + pub fn iter(&self) -> impl Iterator> { self.0.iter() } + + /// Return an iterator over mutable references to the items. + pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } + + /// Consume and return an iterator over the items. + pub fn into_iter(self) -> impl Iterator> { self.0.into_iter() } + /// Consume and either return `self` if it contains some instructions, or if it's empty, then /// instead return the result of `f`. pub fn or_else(self, f: impl FnOnce() -> Self) -> Self { @@ -118,6 +137,18 @@ impl Xcm { } } +impl From>> for Xcm { + fn from(c: Vec>) -> Self { + Self(c) + } +} + +impl From> for Vec> { + fn from(c: Xcm) -> Self { + c.0 + } +} + /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { mod contents { @@ -135,8 +166,8 @@ pub mod prelude { MultiAssetFilter::{self, *}, MultiAssets, MultiLocation, NetworkId::{self, *}, - OriginKind, Outcome, PalletInfo, Parent, ParentThen, QueryId, QueryResponseInfo, - Response, Result as XcmResult, SendError, SendResult, SendXcm, + OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, + QueryResponseInfo, Response, Result as XcmResult, SendError, SendResult, SendXcm, WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 7eb241c1a025..2d4fba7a444c 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -214,8 +214,20 @@ impl Outcome { } } +pub trait PreparedMessage { + fn weight_of(&self) -> Weight; +} + /// Type of XCM message executor. pub trait ExecuteXcm { + type Prepared: PreparedMessage; + fn prepare(message: Xcm) -> result::Result>; + fn execute( + origin: impl Into, + pre: Self::Prepared, + weight_credit: Weight, + ) -> Outcome; + /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is /// a basic hard-limit and the implementation may place further restrictions or requirements on weight and /// other aspects. @@ -244,18 +256,32 @@ pub trait ExecuteXcm { message: Xcm, weight_limit: Weight, weight_credit: Weight, - ) -> Outcome; + ) -> Outcome { + let pre = match Self::prepare(message) { + Ok(x) => x, + Err(_) => return Outcome::Error(Error::WeightNotComputable), + }; + let xcm_weight = pre.weight_of(); + if xcm_weight > weight_limit { + return Outcome::Error(Error::WeightLimitReached(xcm_weight)) + } + Self::execute(origin, pre, weight_credit) + } +} + +pub enum Weightless {} +impl PreparedMessage for Weightless { + fn weight_of(&self) -> Weight { unreachable!() } } impl ExecuteXcm for () { - fn execute_xcm_in_credit( - _origin: impl Into, - _message: Xcm, - _weight_limit: Weight, - _weight_credit: Weight, - ) -> Outcome { - Outcome::Error(Error::Unimplemented) - } + type Prepared = Weightless; + fn prepare(message: Xcm) -> result::Result> { Err(message) } + fn execute( + _: impl Into, + _: Self::Prepared, + _: Weight, + ) -> Outcome { unreachable!() } } /// Error result value when attempting to send an XCM message. diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 41b081a51d15..e4b4701a44bc 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -77,47 +77,36 @@ mod universal_exports { use xcm_executor::traits::ExportXcm; fn ensure_is_remote( - ancestry: impl Into, + universal_local: impl Into, dest: impl Into, - ) -> Result<(NetworkId, InteriorMultiLocation), MultiLocation> { + ) -> Result<(NetworkId, InteriorMultiLocation, NetworkId, InteriorMultiLocation), MultiLocation> { let dest = dest.into(); - let ancestry = ancestry.into(); - let local_network = match ancestry.first() { - Some(GlobalConsensus(network)) => network.clone(), + let universal_local = universal_local.into(); + let (local_net, local_loc) = match universal_local.clone().split_first() { + (location, Some(GlobalConsensus(network))) => (network, location), _ => return Err(dest), }; - let universal_destination: InteriorMultiLocation = ancestry + let universal_destination: InteriorMultiLocation = universal_local .into_location() .appended_with(dest.clone()) .map_err(|x| x.1)? .try_into()?; let (remote_dest, remote_net) = match universal_destination.split_first() { - (d, Some(GlobalConsensus(n))) if n != local_network => (d, n), + (d, Some(GlobalConsensus(n))) if n != local_net => (d, n), _ => return Err(dest), }; - Ok((remote_net, remote_dest)) - } - - pub struct LocalExporter(PhantomData<(Exporter, Ancestry)>); - impl> SendXcm for LocalExporter { - fn send_xcm(dest: impl Into, message: Xcm<()>) -> SendResult { - let (network, destination) = match ensure_is_remote(Ancestry::get(), dest) { - Ok(x) => x, - Err(dest) => return Err(SendError::CannotReachDestination(dest, message)), - }; - Exporter::export_xcm(network, 0, destination, message) - } + Ok((remote_net, remote_dest, local_net, local_loc)) } #[test] - fn local_exporter_works() { + fn ensure_is_remote_works() { // A Kusama parachain is remote from the Polkadot Relay. let x = ensure_is_remote(Polkadot, (Parent, Kusama, Parachain(1000))); - assert_eq!(x, Ok((Kusama, Parachain(1000).into()))); + assert_eq!(x, Ok((Kusama, Parachain(1000).into(), Polkadot, Here))); // Polkadot Relay is remote from a Kusama parachain. let x = ensure_is_remote((Kusama, Parachain(1000)), (Parent, Parent, Polkadot)); - assert_eq!(x, Ok((Polkadot, ().into()))); + assert_eq!(x, Ok((Polkadot, Here, Kusama, Parachain(1000).into()))); // Our own parachain is local. let x = ensure_is_remote(Polkadot, Parachain(1000)); @@ -131,4 +120,69 @@ mod universal_exports { let x = ensure_is_remote((), (Parent, Polkadot, Parachain(1000))); assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); } + + pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancestry)>); + impl> SendXcm for LocalUnpaidExporter { + fn send_xcm(dest: impl Into, message: Xcm<()>) -> SendResult { + let (network, destination, _, _) = match ensure_is_remote(Ancestry::get(), dest) { + Ok(x) => x, + Err(dest) => return Err(SendError::CannotReachDestination(dest, message)), + }; + Exporter::export_xcm(network, 0, destination, message) + } + } + + pub struct LocalUnpaidExecutingExporter< + Executer, + Ancestry, + WeightLimit, + Call, + >(PhantomData<(Executer, Ancestry, WeightLimit, Call)>); + impl< + Executer: ExecuteXcm, + Ancestry: Get, + WeightLimit: Get, + Call, + > SendXcm for LocalUnpaidExecutingExporter { + fn send_xcm(dest: impl Into, mut xcm: Xcm<()>) -> SendResult { + let dest = dest.into(); + + // TODO: proper matching so we can be sure that it's the only viable send_xcm before we + // attempt and thus can acceptably consume dest & xcm. + let err = Err(SendError::CannotReachDestination(dest.clone(), xcm.clone())); + + let devolved = match ensure_is_remote(Ancestry::get(), dest.clone()) { + Ok(x) => x, + Err(dest) => return err, + }; + let (remote_network, remote_location, local_network, local_location) = devolved; + + let mut inner_xcm: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + inner_xcm.inner_mut().extend(xcm.into_iter()); + + let message = Xcm(vec![ +/* WithdrawAsset((Here, )), + BuyExecution { fees: Wild(AllCounted(1)), weight_limit: Unlimited }, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, +*/ ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }, + ]); + let dest = dest.into(); + let pre = match Executer::prepare(message) { + Ok(x) => x, + Err(_) => return err, + }; + // We just swallow the weight - it should be constant. + let weight_credit = pre.weight_of(); + match Executer::execute(Here, pre, weight_credit) { + Outcome::Complete(_) => Ok(()), + _ => return err, + } + } + } + + // TODO: LocalPaidExecutingExporter able to accept from non-Here origins. + // TODO: RemotePaidExecutingExporter which uses `SendXcm`. } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index ed3975b83f6a..e9f521263fed 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -63,51 +63,37 @@ pub struct XcmExecutor { _config: PhantomData, } +pub struct WeighedMessage(Weight, Xcm); +impl PreparedMessage for WeighedMessage { + fn weight_of(&self) -> Weight { + self.0 + } +} + impl ExecuteXcm for XcmExecutor { - fn execute_xcm_in_credit( - origin: impl Into, + type Prepared = WeighedMessage; + fn prepare( mut message: Xcm, - weight_limit: Weight, + ) -> Result> { + match Config::Weigher::weight(&mut message) { + Ok(weight) => Ok(WeighedMessage(weight, message)), + Err(_) => Err(message), + } + } + fn execute( + origin: impl Into, + WeighedMessage(xcm_weight, mut message): WeighedMessage, mut weight_credit: Weight, ) -> Outcome { let origin = origin.into(); log::trace!( target: "xcm::execute_xcm_in_credit", - "origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?}", + "origin: {:?}, message: {:?}, weight_credit: {:?}", origin, message, - weight_limit, weight_credit, ); - let xcm_weight = match Config::Weigher::weight(&mut message) { - Ok(x) => x, - Err(()) => { - log::debug!( - target: "xcm::execute_xcm_in_credit", - "Weight not computable! (origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?})", - origin, - message, - weight_limit, - weight_credit, - ); - return Outcome::Error(XcmError::WeightNotComputable) - }, - }; - if xcm_weight > weight_limit { - log::debug!( - target: "xcm::execute_xcm_in_credit", - "Weight limit reached! weight > weight_limit: {:?} > {:?}. (origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?})", - xcm_weight, - weight_limit, - origin, - message, - weight_limit, - weight_credit, - ); - return Outcome::Error(XcmError::WeightLimitReached(xcm_weight)) - } - - if let Err(e) = + if let Err(_) = Config::Barrier::should_execute(&origin, &mut message, xcm_weight, &mut weight_credit) { log::debug!( From 128363e22b340897967b4e91985128b1d48299e0 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 2 Nov 2021 17:12:46 +0100 Subject: [PATCH 13/57] Update xcm/src/v3/mod.rs Co-authored-by: Keith Yeung --- xcm/src/v3/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 56269be4aef8..29e48c8549f7 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -80,10 +80,10 @@ impl Xcm { } /// Return a reference to the inner value. - pub fn inner(&self) -> &Vec> { &self.0 } + pub fn inner(&self) -> &[Instruction] { &self.0 } /// Return a mutable reference to the inner value. - pub fn inner_mut(&mut self) -> &mut Vec> { &mut self.0 } + pub fn inner_mut(&mut self) -> &mut [Instruction] { &mut self.0 } /// Consume and return the inner value. pub fn into_inner(self) -> Vec> { self.0 } From 4eb42d66f5b93e4ef153df4c1404f19795af2386 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 10 Jan 2022 13:40:43 +0100 Subject: [PATCH 14/57] XCM crate building again --- xcm/procedural/src/v3.rs | 85 +++++++++--- xcm/src/v3/multiasset.rs | 67 +++++++--- xcm/src/v3/multilocation.rs | 248 +++++++++++++++++++++++++++++++----- 3 files changed, 330 insertions(+), 70 deletions(-) diff --git a/xcm/procedural/src/v3.rs b/xcm/procedural/src/v3.rs index 2c2ab1a19fe5..52f8733b698d 100644 --- a/xcm/procedural/src/v3.rs +++ b/xcm/procedural/src/v3.rs @@ -23,15 +23,12 @@ const MAX_JUNCTIONS: usize = 8; pub mod multilocation { use super::*; - // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. - const MAX_PARENTS: usize = 8; - pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { if !input.is_empty() { return Err(syn::Error::new(Span::call_site(), "No arguments expected")) } - let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS, MAX_PARENTS); + let from_tuples = generate_conversion_from_tuples(8, 8); Ok(quote! { #from_tuples @@ -39,11 +36,12 @@ pub mod multilocation { } fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream { - (0..=max_junctions) + let mut from_tuples = (0..=max_junctions) .map(|num_junctions| { let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::>(); let idents = (0..num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); + let array_size = num_junctions; let interior = if num_junctions == 0 { quote!(Junctions::Here) } else { @@ -53,25 +51,70 @@ pub mod multilocation { } }; - (0..=max_parents) - .map(|cur_parents| { - let parents = - (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); - let underscores = (0..cur_parents) - .map(|_| Token![_](Span::call_site())) - .collect::>(); - - quote! { - impl< #(#types : Into,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation { - fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self { - Self { parents: #cur_parents as u8, interior: #interior } - } + let mut from_tuple = quote! { + impl< #(#types : Into,)* > From<( Ancestor, #( #types ),* )> for MultiLocation { + fn from( ( Ancestor(parents), #(#idents),* ): ( Ancestor, #( #types ),* ) ) -> Self { + MultiLocation { parents, interior: #interior } + } + } + + impl From<[Junction; #array_size]> for MultiLocation { + fn from(j: [Junction; #array_size]) -> Self { + let [#(#idents),*] = j; + MultiLocation { parents: 0, interior: #interior } + } + } + }; + + let from_parent_tuples = (0..=max_parents).map(|cur_parents| { + let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); + let underscores = + (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); + + quote! { + impl< #(#types : Into,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation { + fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self { + Self { parents: #cur_parents as u8, interior: #interior } } } - }) - .collect::() + } + }); + + from_tuple.extend(from_parent_tuples); + from_tuple }) - .collect() + .collect::(); + + let from_parent_junctions_tuples = (0..=max_parents).map(|cur_parents| { + let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); + let underscores = + (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); + + quote! { + impl From<( #(#parents,)* Junctions )> for MultiLocation { + fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self { + MultiLocation { parents: #cur_parents as u8, interior: junctions } + } + } + } + }); + from_tuples.extend(from_parent_junctions_tuples); + + quote! { + impl From<(Ancestor, Junctions)> for MultiLocation { + fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self { + MultiLocation { parents, interior } + } + } + + impl From for MultiLocation { + fn from(x: Junction) -> Self { + MultiLocation { parents: 0, interior: Junctions::X1(x) } + } + } + + #from_tuples + } } } diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index dedc8698ebd0..7f9e0ea175b9 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -70,13 +70,22 @@ impl TryFrom for AssetId { impl AssetId { /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. - pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> { if let AssetId::Concrete(ref mut l) = self { l.prepend_with(prepend.clone()).map_err(|_| ())?; } Ok(()) } + /// Mutate the asset to represent the same value from the perspective of a new `target` + /// location. The local chain's location is provided in `ancestry`. + pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + if let AssetId::Concrete(ref mut l) = self { + l.reanchor(target, ancestry)?; + } + Ok(()) + } + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value. pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset { MultiAsset { fun, id: self } @@ -129,13 +138,24 @@ impl MultiAsset { } /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. - pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - self.id.reanchor(prepend) + pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + self.id.prepend_with(prepend) } - /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. - pub fn reanchored(mut self, prepend: &MultiLocation) -> Result { - self.reanchor(prepend)?; + /// Mutate the location of the asset identifier if concrete, giving it the same location + /// relative to a `target` context. The local context is provided as `ancestry`. + pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + self.id.reanchor(target, ancestry) + } + + /// Mutate the location of the asset identifier if concrete, giving it the same location + /// relative to a `target` context. The local context is provided as `ancestry`. + pub fn reanchored( + mut self, + target: &MultiLocation, + ancestry: &MultiLocation, + ) -> Result { + self.id.reanchor(target, ancestry)?; Ok(self) } @@ -195,8 +215,10 @@ impl From> for MultiAssets { ( MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id }, MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id }, - ) if a_id == b_id => - MultiAsset { id: a_id, fun: Fungibility::Fungible(a_amount + b_amount) }, + ) if a_id == b_id => MultiAsset { + id: a_id, + fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)), + }, ( MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id }, @@ -307,8 +329,14 @@ impl MultiAssets { } /// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location. - pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - self.0.iter_mut().try_for_each(|i| i.reanchor(prepend)) + pub fn prepend_with(&mut self, prefix: &MultiLocation) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix)) + } + + /// Mutate the location of the asset identifier if concrete, giving it the same location + /// relative to a `target` context. The local context is provided as `ancestry`. + pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.reanchor(target, ancestry)) } /// Return a reference to an item at a specific index or `None` if it doesn't exist. @@ -361,11 +389,11 @@ impl TryFrom<(OldWildMultiAsset, u32)> for WildMultiAsset { } impl WildMultiAsset { - /// Returns true if the wild element of `self` matches `inner`. + /// Returns true if `self` is a super-set of the given `inner`. /// - /// Note that for `Counted` variants of wildcards, then it will disregard the count except for - /// always returning `false` when equal to 0. - pub fn matches(&self, inner: &MultiAsset) -> bool { + /// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard. + /// For more details, see the implementation and tests. + pub fn contains(&self, inner: &MultiAsset) -> bool { use WildMultiAsset::*; match self { AllOfCounted { count: 0, .. } | AllCounted(0) => false, @@ -375,6 +403,15 @@ impl WildMultiAsset { } } + /// Returns true if the wild element of `self` matches `inner`. + /// + /// Note that for `Counted` variants of wildcards, then it will disregard the count except for + /// always returning `false` when equal to 0. + #[deprecated = "Use `contains` instead"] + pub fn matches(&self, inner: &MultiAsset) -> bool { + self.contains(inner) + } + /// Mutate the asset to represent the same value from the perspective of a new `target` /// location. The local chain's location is provided in `ancestry`. pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { @@ -456,7 +493,7 @@ impl MultiAssetFilter { pub fn matches(&self, inner: &MultiAsset) -> bool { match self { MultiAssetFilter::Definite(ref assets) => assets.contains(inner), - MultiAssetFilter::Wild(ref wild) => wild.matches(inner), + MultiAssetFilter::Wild(ref wild) => wild.contains(inner), } } diff --git a/xcm/src/v3/multilocation.rs b/xcm/src/v3/multilocation.rs index 42ff49d8f309..5588f9191c2f 100644 --- a/xcm/src/v3/multilocation.rs +++ b/xcm/src/v3/multilocation.rs @@ -372,6 +372,60 @@ impl MultiLocation { Err(prefix) => Err((self, prefix)), } } + + /// Mutate `self` so that it represents the same location from the point of view of `target`. + /// The context of `self` is provided as `ancestry`. + /// + /// Does not modify `self` in case of overflow. + pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + // TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this. + + // 1. Use our `ancestry` to figure out how the `target` would address us. + let inverted_target = ancestry.inverted(target)?; + + // 2. Prepend `inverted_target` to `self` to get self's location from the perspective of + // `target`. + self.prepend_with(inverted_target).map_err(|_| ())?; + + // 3. Given that we know some of `target` ancestry, ensure that any parents in `self` are + // strictly needed. + self.simplify(target.interior()); + + Ok(()) + } + + /// Treating `self` as a context, determine how it would be referenced by a `target` location. + pub fn inverted(&self, target: &MultiLocation) -> Result { + use Junction::OnlyChild; + let mut ancestry = self.clone(); + let mut junctions = Junctions::Here; + for _ in 0..target.parent_count() { + junctions = junctions + .pushed_front_with(ancestry.interior.take_last().unwrap_or(OnlyChild)) + .map_err(|_| ())?; + } + let parents = target.interior().len() as u8; + Ok(MultiLocation::new(parents, junctions)) + } + + /// Remove any unneeded parents/junctions in `self` based on the given context it will be + /// interpreted in. + pub fn simplify(&mut self, context: &Junctions) { + if context.len() < self.parents as usize { + // Not enough context + return + } + while self.parents > 0 { + let maybe = context.at(context.len() - (self.parents as usize)); + match (self.interior.first(), maybe) { + (Some(i), Some(j)) if i == j => { + self.interior.take_first(); + self.parents -= 1; + }, + _ => break, + } + } + } } impl TryFrom for MultiLocation { @@ -417,18 +471,6 @@ impl> From> for MultiLocation { } } -impl From for MultiLocation { - fn from(t: Junctions) -> Self { - Self { parents: 0, interior: t } - } -} - -impl From for MultiLocation { - fn from(t: Junction) -> Self { - Self { parents: 0, interior: Junctions::X1(t) } - } -} - xcm_procedural::impl_conversion_functions_for_multilocation_v3!(); #[cfg(test)] @@ -440,10 +482,10 @@ mod tests { fn conversion_works() { let x: MultiLocation = Parent.into(); assert_eq!(x, MultiLocation { parents: 1, interior: Here }); - let x: MultiLocation = (Parent,).into(); - assert_eq!(x, MultiLocation { parents: 1, interior: Here }); - let x: MultiLocation = (Parent, Parent).into(); - assert_eq!(x, MultiLocation { parents: 2, interior: Here }); +// let x: MultiLocation = (Parent,).into(); +// assert_eq!(x, MultiLocation { parents: 1, interior: Here }); +// let x: MultiLocation = (Parent, Parent).into(); +// assert_eq!(x, MultiLocation { parents: 2, interior: Here }); let x: MultiLocation = (Parent, Parent, OnlyChild).into(); assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() }); let x: MultiLocation = OnlyChild.into(); @@ -452,9 +494,85 @@ mod tests { assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() }); } + #[test] + fn inverted_works() { + let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42)).into(); + let target = (Parent, PalletInstance(69)).into(); + let expected = (Parent, PalletInstance(42)).into(); + let inverted = ancestry.inverted(&target).unwrap(); + assert_eq!(inverted, expected); + + let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42), GeneralIndex(1)).into(); + let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into(); + let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into(); + let inverted = ancestry.inverted(&target).unwrap(); + assert_eq!(inverted, expected); + } + + #[test] + fn simplify_basic_works() { + let mut location: MultiLocation = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = X2(Parachain(1000), PalletInstance(42)); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into(); + let context = X1(PalletInstance(42)); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into(); + let context = X2(Parachain(1000), PalletInstance(42)); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: MultiLocation = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = X3(OnlyChild, Parachain(1000), PalletInstance(42)); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + } + + #[test] + fn simplify_incompatible_location_fails() { + let mut location: MultiLocation = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42)); + let expected = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: MultiLocation = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = X1(Parachain(1000)); + let expected = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + location.simplify(&context); + assert_eq!(location, expected); + } + + #[test] + fn reanchor_works() { + let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into(); + let ancestry = Parachain(2000).into(); + let target = (Parent, Parachain(1000)).into(); + let expected = GeneralIndex(42).into(); + id.reanchor(&target, &ancestry).unwrap(); + assert_eq!(id, expected); + } + #[test] fn encode_and_decode_works() { - let m: MultiLocation = (Parent, Parachain(42), 23u64).into(); + let m = MultiLocation { + parents: 1, + interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }), + }; let encoded = m.encode(); assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec()); let decoded = MultiLocation::decode(&mut &encoded[..]); @@ -463,45 +581,69 @@ mod tests { #[test] fn match_and_split_works() { - let m: MultiLocation = (Parent, Parachain(42), 23u64).into(); - assert_eq!(m.match_and_split(&Parent.into()), None); - assert_eq!(m.match_and_split(&(Parent, Parachain(42)).into()), Some(&23u64.into())); + let m = MultiLocation { + parents: 1, + interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }), + }; + assert_eq!(m.match_and_split(&MultiLocation { parents: 1, interior: Here }), None); + assert_eq!( + m.match_and_split(&MultiLocation { parents: 1, interior: X1(Parachain(42)) }), + Some(&AccountIndex64 { network: None, index: 23 }) + ); assert_eq!(m.match_and_split(&m), None); } #[test] fn append_with_works() { - let acc: Junction = 23u64.into(); - let mut m: MultiLocation = (Parent, Parachain(42)).into(); - assert_eq!(m.append_with((PalletInstance(3), acc.clone())), Ok(())); - assert_eq!(m, MultiLocation::from((Parent, Parachain(42), PalletInstance(3), acc.clone()))); + let acc = AccountIndex64 { network: None, index: 23 }; + let mut m = MultiLocation { parents: 1, interior: X1(Parachain(42)) }; + assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(())); + assert_eq!( + m, + MultiLocation { + parents: 1, + interior: X3(Parachain(42), PalletInstance(3), acc.clone()) + } + ); // cannot append to create overly long multilocation - let acc: Junction = 23u64.into(); + let acc = AccountIndex64 { network: None, index: 23 }; let m = MultiLocation { parents: 254, - interior: (Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild).into(), + interior: X5(Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild), }; - let suffix = (PalletInstance(3), acc.clone(), OnlyChild, OnlyChild); - assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix.into())); + let suffix: MultiLocation = (PalletInstance(3), acc.clone(), OnlyChild, OnlyChild).into(); + assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix)); } #[test] fn prepend_with_works() { - let mut m: MultiLocation = (Parent, Parachain(42), 23u64).into(); - assert_eq!(m.prepend_with((Parent, OnlyChild)), Ok(())); - assert_eq!(m, MultiLocation::from((Parent, Parachain(42), 23u64))); + let mut m = MultiLocation { + parents: 1, + interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }), + }; + assert_eq!(m.prepend_with(MultiLocation { parents: 1, interior: X1(OnlyChild) }), Ok(())); + assert_eq!( + m, + MultiLocation { + parents: 1, + interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }) + } + ); // cannot prepend to create overly long multilocation let mut m = MultiLocation { parents: 254, interior: X1(Parachain(42)) }; - assert_eq!(m.prepend_with((Parent, Parent)), Err((Parent, Parent).into())); - assert_eq!(m.prepend_with(Parent), Ok(())); + let prefix = MultiLocation { parents: 2, interior: Here }; + assert_eq!(m.prepend_with(prefix.clone()), Err(prefix)); + + let prefix = MultiLocation { parents: 1, interior: Here }; + assert_eq!(m.prepend_with(prefix), Ok(())); assert_eq!(m, MultiLocation { parents: 255, interior: X1(Parachain(42)) }); } #[test] fn double_ended_ref_iteration_works() { - let m: Junctions = (Parachain(1000), Parachain(3), PalletInstance(5)).into(); + let m = X3(Parachain(1000), Parachain(3), PalletInstance(5)); let mut iter = m.iter(); let first = iter.next().unwrap(); @@ -529,4 +671,42 @@ mod tests { assert_eq!(iter.next(), None); assert_eq!(iter.next_back(), None); } + + #[test] + fn conversion_from_other_types_works() { + use crate::v1; + use core::convert::TryInto; + + fn takes_multilocation>(_arg: Arg) {} + + takes_multilocation(Parent); + takes_multilocation(Here); + takes_multilocation(X1(Parachain(42))); + takes_multilocation((Ancestor(255), PalletInstance(8))); + takes_multilocation((Ancestor(5), Parachain(1), PalletInstance(3))); + takes_multilocation((Ancestor(2), Here)); + takes_multilocation(AncestorThen( + 3, + X2(Parachain(43), AccountIndex64 { network: None, index: 155 }), + )); + takes_multilocation((Parent, AccountId32 { network: None, id: [0; 32] })); + takes_multilocation((Parent, Here)); + takes_multilocation(ParentThen(X1(Parachain(75)))); + takes_multilocation([Parachain(100), PalletInstance(3)]); + + assert_eq!(v1::MultiLocation::from(v1::Junctions::Here).try_into(), Ok(MultiLocation::here())); + assert_eq!( + v1::MultiLocation::from(v1::Parent).try_into(), + Ok(MultiLocation::parent()) + ); + assert_eq!( + v1::MultiLocation::from(( + v1::Parent, + v1::Parent, + v1::Junction::GeneralKey(b"foo".to_vec()), + )) + .try_into(), + Ok(MultiLocation { parents: 2, interior: X1(GeneralKey(b"foo".to_vec())) }), + ); + } } From 08c41fac4c417f1f245707c58e77d971e58c00ac Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 Jan 2022 10:22:47 +0100 Subject: [PATCH 15/57] Initial bridging primitive --- runtime/polkadot/src/lib.rs | 16 +++++++++------- runtime/westend/src/lib.rs | 2 +- xcm/pallet-xcm-benchmarks/src/generic/mock.rs | 7 +++++-- xcm/pallet-xcm/src/lib.rs | 4 ++-- xcm/src/v3/mod.rs | 2 +- xcm/xcm-builder/src/lib.rs | 7 +++---- xcm/xcm-executor/src/lib.rs | 10 +++++----- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 4ca484dab666..ce11ed147cbd 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -1314,12 +1314,11 @@ parameter_types! { /// The location of the DOT token, from the context of this chain. Since this token is native to this /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to /// the context". - pub const DotLocation: MultiLocation = Here.into(); + pub const DotLocation: MultiLocation = Here.into_location(); /// The Polkadot network ID. This is named. pub const PolkadotNetwork: NetworkId = NetworkId::Polkadot; - /// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since - /// Polkadot is a top-level relay-chain, there is no ancestry. - pub const Ancestry: MultiLocation = Here.into(); + /// Our location in the universe of consensus systems. + pub const UniversalLocation: InteriorMultiLocation = X1(GlobalConsensus(NetworkId::Polkadot)); /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -1377,7 +1376,7 @@ pub type XcmRouter = ( parameter_types! { pub const Polkadot: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(DotLocation::get()) }); - pub const PolkadotForStatemint: (MultiAssetFilter, MultiLocation) = (Polkadot::get(), Parachain(1000).into()); + pub const PolkadotForStatemint: (MultiAssetFilter, MultiLocation) = (Polkadot::get(), Parachain(1000).into_location()); pub const MaxAssetsIntoHolding: u32 = 64; } @@ -1409,7 +1408,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = LocalOriginConverter; type IsReserve = (); type IsTeleporter = TrustedTeleporters; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. @@ -1420,6 +1419,9 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + // No bridges yet... + type MessageExporter = (); + type UniversalAliases = Nothing; } parameter_types! { @@ -1454,7 +1456,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Nothing; type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 5f7bc394d6a1..d8a781229f01 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1018,7 +1018,7 @@ pub type XcmRouter = ( parameter_types! { pub const Westmint: MultiLocation = Parachain(1000).into_location(); - pub const Encointer: MultiLocation = Parachain(1001).into(); + pub const Encointer: MultiLocation = Parachain(1001).into_location(); pub const Wnd: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); pub const WndForWestmint: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Westmint::get()); pub const WndForEncointer: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Encointer::get()); diff --git a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index 7f93c4c93a20..0da55bd406c3 100644 --- a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -19,7 +19,7 @@ use crate::{generic, mock::*, *}; use frame_support::{ parameter_types, - traits::{Everything, OriginTrait}, + traits::{Everything, Nothing, OriginTrait}, }; use sp_core::H256; use sp_runtime::{ @@ -115,6 +115,9 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = TestSubscriptionService; type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + // No bridges yet... + type MessageExporter = (); + type UniversalAliases = Nothing; } impl crate::Config for Test { @@ -122,7 +125,7 @@ impl crate::Config for Test { type AccountIdConverter = AccountIdConverter; fn valid_destination() -> Result { let valid_destination: MultiLocation = - Junction::AccountId32 { network: NetworkId::Any, id: [0u8; 32] }.into(); + Junction::AccountId32 { network: None, id: [0u8; 32] }.into(); Ok(valid_destination) } diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 5df4aade835a..f2e8fea615c8 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -782,7 +782,7 @@ pub mod pallet { let value = (origin_location, assets.drain()); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; - let ancestry = T::LocationInverter::ancestry(); + let ancestry = T::LocationInverter::universal_location().into(); let fees = assets .get(fee_asset_item as usize) .ok_or(Error::::Empty)? @@ -839,7 +839,7 @@ pub mod pallet { let value = (origin_location, assets.drain()); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; - let ancestry = T::LocationInverter::ancestry(); + let ancestry = T::LocationInverter::universal_location().into(); let fees = assets .get(fee_asset_item as usize) .ok_or(Error::::Empty)? diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 29e48c8549f7..1f933feb8046 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -83,7 +83,7 @@ impl Xcm { pub fn inner(&self) -> &[Instruction] { &self.0 } /// Return a mutable reference to the inner value. - pub fn inner_mut(&mut self) -> &mut [Instruction] { &mut self.0 } + pub fn inner_mut(&mut self) -> &mut Vec> { &mut self.0 } /// Consume and return the inner value. pub fn into_inner(self) -> Vec> { self.0 } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index e4b4701a44bc..68e367e713d7 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -144,16 +144,16 @@ mod universal_exports { WeightLimit: Get, Call, > SendXcm for LocalUnpaidExecutingExporter { - fn send_xcm(dest: impl Into, mut xcm: Xcm<()>) -> SendResult { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { let dest = dest.into(); // TODO: proper matching so we can be sure that it's the only viable send_xcm before we // attempt and thus can acceptably consume dest & xcm. let err = Err(SendError::CannotReachDestination(dest.clone(), xcm.clone())); - let devolved = match ensure_is_remote(Ancestry::get(), dest.clone()) { + let devolved = match ensure_is_remote(Ancestry::get(), dest) { Ok(x) => x, - Err(dest) => return err, + Err(_) => return err, }; let (remote_network, remote_location, local_network, local_location) = devolved; @@ -169,7 +169,6 @@ mod universal_exports { DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, */ ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }, ]); - let dest = dest.into(); let pre = match Executer::prepare(message) { Ok(x) => x, Err(_) => return err, diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index e9f521263fed..43a786c26dfa 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -93,16 +93,15 @@ impl ExecuteXcm for XcmExecutor { message, weight_credit, ); - if let Err(_) = + if let Err(e) = Config::Barrier::should_execute(&origin, &mut message, xcm_weight, &mut weight_credit) { log::debug!( target: "xcm::execute_xcm_in_credit", - "Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?})", + "Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, weight_credit: {:?})", e, origin, message, - weight_limit, weight_credit, ); return Outcome::Error(XcmError::Barrier) @@ -314,7 +313,7 @@ impl XcmExecutor { for asset in assets.inner() { Config::AssetTransactor::beam_asset(asset, origin, &dest)?; } - let ancestry = Config::LocationInverter::ancestry(); + let ancestry = Config::LocationInverter::universal_location().into(); assets.reanchor(&dest, &ancestry).map_err(|()| XcmError::MultiLocationFull)?; let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); @@ -594,7 +593,8 @@ impl XcmExecutor { dest: &MultiLocation, maybe_failed_bin: Option<&mut Assets>, ) -> MultiAssets { - assets.reanchor(dest, &Config::LocationInverter::ancestry(), maybe_failed_bin); + let ancestry = Config::LocationInverter::universal_location().into(); + assets.reanchor(dest, &ancestry, maybe_failed_bin); assets.into_assets_iter().collect::>().into() } } From 562b08bb9052b6e4164e957b047fb1d884ee39ab Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 17 Jan 2022 14:33:44 +0100 Subject: [PATCH 16/57] Docs --- runtime/kusama/src/lib.rs | 2 +- xcm/xcm-builder/src/mock.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 987954dc6229..ffda49dd22e2 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1357,7 +1357,7 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter< CheckAccount, >; -/// The means that we convert an the XCM message origin location into a local dispatch origin. +/// The means that we convert the XCM message origin location into a local dispatch origin. type LocalOriginConverter = ( // A `Signed` origin of the sovereign account that the original location controls. SovereignSignedViaLocation, diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index ecd9b5178118..eeadd2c75068 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -36,7 +36,6 @@ pub use sp_std::{ marker::PhantomData, }; pub use xcm::latest::prelude::*; -use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ traits::{ ConvertOrigin, ExportXcm, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset, From 5ac926abd4eed43e7a92c183e0d82699b6810891 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 18 Jan 2022 14:50:31 +0100 Subject: [PATCH 17/57] Docs --- xcm/src/v3/mod.rs | 3 +++ xcm/xcm-executor/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 1f933feb8046..09903a718de1 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -754,6 +754,9 @@ pub enum Instruction { /// child which is the root of the local consensus system since it would by extension /// allow it to act as any location within the local consensus. /// + /// The `Junction` parameter should generally be a `GlobalConsensus` variant since it is only + /// these which are children of the Universal Ancestor. + /// /// Kind: *Instruction* /// /// Errors: *Fallible*. diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 43a786c26dfa..67de3ec18313 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -565,7 +565,7 @@ impl XcmExecutor { }, ExportMessage { network, destination, xcm } => { // Hash identifies the lane on the exporter which we use. We use the pairwise - // combination of the origin and destination ensure origin/destination pairs will + // combination of the origin and destination to ensure origin/destination pairs will // generally have their own lanes. let hash = (&self.origin, &destination).using_encoded(blake2_128); let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); From b2dc3f13df69a00247af6f6bb59d429b0db02ba4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 Jan 2022 12:44:42 +0100 Subject: [PATCH 18/57] More work --- Cargo.lock | 1 + xcm/xcm-builder/Cargo.toml | 1 + xcm/xcm-builder/src/lib.rs | 159 +++++++++++++++++++++++++++++++++++-- 3 files changed, 153 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5274ac533030..5709662e94c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11942,6 +11942,7 @@ version = "0.9.13" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "pallet-balances", "pallet-transaction-payment", diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index bf776f73b7fa..5165f70e288e 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -6,6 +6,7 @@ description = "Tools & types for building with XCM and its executor." version = "0.9.13" [dependencies] +impl-trait-for-tuples = "0.2.1" parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] } scale-info = { version = "1.0", default-features = false, features = ["derive"] } xcm = { path = "..", default-features = false } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 68e367e713d7..c7867dea2255 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -121,17 +121,33 @@ mod universal_exports { assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); } + /// Implementation of `SendXcm` which uses the given `ExportXcm` impl in order to forward the + /// message over a bridge. + /// + /// The actual message forwarded over the bridge is prepended with `UniversalOrigin` and + /// `DescendOrigin` in order to ensure that the message is executed with this Origin. + /// + /// No effort is made to charge for any bridge fees, so this can only be used when it is known + /// that the message sending cannot be abused in any way. pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancestry)>); impl> SendXcm for LocalUnpaidExporter { - fn send_xcm(dest: impl Into, message: Xcm<()>) -> SendResult { - let (network, destination, _, _) = match ensure_is_remote(Ancestry::get(), dest) { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let devolved = match ensure_is_remote(Ancestry::get(), dest) { Ok(x) => x, - Err(dest) => return Err(SendError::CannotReachDestination(dest, message)), + Err(dest) => return Err(SendError::CannotReachDestination(dest, xcm)), }; + let (network, destination, local_network, local_location) = devolved; + let mut message: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + message.inner_mut().extend(xcm.into_iter()); Exporter::export_xcm(network, 0, destination, message) } } + /// Alternative implentation of `LocalUnpaidExporter` which uses the `ExportMessage` + /// instruction executing locally. pub struct LocalUnpaidExecutingExporter< Executer, Ancestry, @@ -144,6 +160,135 @@ mod universal_exports { WeightLimit: Get, Call, > SendXcm for LocalUnpaidExecutingExporter { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let dest = dest.into(); + + // TODO: proper matching so we can be sure that it's the only viable send_xcm before we + // attempt and thus can acceptably consume dest & xcm. + let err = Err(SendError::CannotReachDestination(dest.clone(), xcm.clone())); + + let devolved = match ensure_is_remote(Ancestry::get(), dest) { + Ok(x) => x, + Err(_) => return err, + }; + let (remote_network, remote_location, local_network, local_location) = devolved; + + let mut inner_xcm: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + inner_xcm.inner_mut().extend(xcm.into_iter()); + + let message = Xcm(vec![ ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, + } ]); + let pre = match Executer::prepare(message) { + Ok(x) => x, + Err(_) => return err, + }; + // We just swallow the weight - it should be constant. + let weight_credit = pre.weight_of(); + match Executer::execute(Here, pre, weight_credit) { + Outcome::Complete(_) => Ok(()), + _ => return err, + } + } + } + + // TODO: `LocalPaidExecutingExporter` able to accept from non-`Here`, but local, origins. + + pub trait ExporterFor { + fn exporter_for(network: &NetworkId, remote_location: &InteriorMultiLocation) -> Option; + } + + #[impl_trait_for_tuples::impl_for_tuples(30)] + impl ExporterFor for Tuple { + fn exporter_for(network: &NetworkId, remote_location: &InteriorMultiLocation) -> Option { + for_tuples!( #( + if let Some(r) = Tuple::exporter_for(network, remote_location) { + return Some(r); + } + )* ); + None + } + } + + pub struct NetworkExportTable(sp_std::marker::PhantomData); + impl> ExporterFor for NetworkExportTable { + fn exporter_for(network: &NetworkId, _: &InteriorMultiLocation) -> Option { + T::get().iter().find(|(ref j, _)| j == network).map(|(_, l)| l.clone()) + } + } + + /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction + /// and sends it to a destination known to be able to handle it. + /// + /// No effort is made to make payment to the bridge for its services, so the bridge location + /// must have been configured with a barrier rule allowing unpaid execution for this message + /// coming from our origin. + /// + /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` + /// and `DescendOrigin` in order to ensure that the message is executed with our Origin. + pub struct UnpaidRemoteExporter< + Bridges, + Router, + Ancestry, + >(PhantomData<(Bridges, Router, Ancestry)>); + impl< + Bridges: ExporterFor, + Router: SendXcm, + Ancestry: Get, + > SendXcm for UnpaidRemoteExporter { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let dest = dest.into(); + + // TODO: proper matching so we can be sure that it's the only viable send_xcm before we + // attempt and thus can acceptably consume dest & xcm. + let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); + + let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; + let (remote_network, remote_location, local_network, local_location) = devolved; + + let bridge = Bridges::exporter_for(&remote_network, &remote_location).ok_or(err)?; + + let mut inner_xcm: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + inner_xcm.inner_mut().extend(xcm.into_iter()); + let message = Xcm(vec![ + ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, + }, + ]); + Router::send_xcm(bridge, message) + } + } +/* + /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction + /// and sends it to a destination known to be able to handle it. + /// + /// No effort is made to make payment to the bridge for its services, so the bridge location + /// must have been configured with a barrier rule allowing this message unpaid execution. + /// + /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` + /// and `DescendOrigin` in order to ensure that the message is executed with this Origin. + pub struct PaidRemoteExporter< + Executer, + Ancestry, + WeightLimit, + Call, + >(PhantomData<(Executer, Ancestry, WeightLimit, Call)>); + impl< + Executer: ExecuteXcm, + Ancestry: Get, + WeightLimit: Get, + Call, + > SendXcm for LocalPaidExecutingExporter { fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { let dest = dest.into(); @@ -164,10 +309,10 @@ mod universal_exports { inner_xcm.inner_mut().extend(xcm.into_iter()); let message = Xcm(vec![ -/* WithdrawAsset((Here, )), + WithdrawAsset((Here, ).into()), BuyExecution { fees: Wild(AllCounted(1)), weight_limit: Unlimited }, DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, -*/ ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }, + ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }, ]); let pre = match Executer::prepare(message) { Ok(x) => x, @@ -181,7 +326,5 @@ mod universal_exports { } } } - - // TODO: LocalPaidExecutingExporter able to accept from non-Here origins. - // TODO: RemotePaidExecutingExporter which uses `SendXcm`. +*/ } From 4d8c9a8fe795faa23f627aea28802ee37c1143a2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 Jan 2022 12:46:27 +0100 Subject: [PATCH 19/57] More work --- xcm/xcm-builder/src/lib.rs | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index c7867dea2255..d874d75e2b20 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -268,6 +268,53 @@ mod universal_exports { Router::send_xcm(bridge, message) } } + + /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction + /// and sends it to a destination known to be able to handle it. + /// + /// No effort is made to make payment to the bridge for its services, so the bridge location + /// must have been configured with a barrier rule allowing unpaid execution for this message + /// coming from our origin. + /// + /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` + /// and `DescendOrigin` in order to ensure that the message is executed with our Origin. + pub struct UnpaidRemoteExporter< + Bridges, + Router, + Ancestry, + >(PhantomData<(Bridges, Router, Ancestry)>); + impl< + Bridges: ExporterFor, + Router: SendXcm, + Ancestry: Get, + > SendXcm for UnpaidRemoteExporter { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let dest = dest.into(); + + // TODO: proper matching so we can be sure that it's the only viable send_xcm before we + // attempt and thus can acceptably consume dest & xcm. + let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); + + let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; + let (remote_network, remote_location, local_network, local_location) = devolved; + + let bridge = Bridges::exporter_for(&remote_network, &remote_location).ok_or(err)?; + + let mut inner_xcm: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + inner_xcm.inner_mut().extend(xcm.into_iter()); + let message = Xcm(vec![ + ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, + }, + ]); + Router::send_xcm(bridge, message) + } + } /* /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction /// and sends it to a destination known to be able to handle it. From 4a8fc7f300d4ea5e8aadd201bbcd2a3c40f104c9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 22 Jan 2022 18:35:30 +0100 Subject: [PATCH 20/57] Merge branch 'gav-xcm-v3' into gav-xcm-v3-bridging --- runtime/kusama/src/xcm_config.rs | 1 + runtime/polkadot/src/xcm_config.rs | 1 + runtime/rococo/src/xcm_config.rs | 1 + runtime/westend/src/tests.rs | 2 +- runtime/westend/src/weights/xcm/mod.rs | 8 ++- runtime/westend/src/xcm_config.rs | 1 + xcm/pallet-xcm/src/tests.rs | 14 ++--- xcm/src/v2/mod.rs | 4 +- xcm/src/v3/mod.rs | 3 +- xcm/src/v3/multilocation.rs | 11 ++++ xcm/src/v3/traits.rs | 79 -------------------------- xcm/xcm-builder/src/mock.rs | 1 - xcm/xcm-builder/src/tests.rs | 2 +- xcm/xcm-builder/tests/scenarios.rs | 2 +- xcm/xcm-executor/src/lib.rs | 2 +- 15 files changed, 36 insertions(+), 96 deletions(-) diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index aba23276bff9..96a2336098a2 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -27,6 +27,7 @@ use frame_support::{ }; use runtime_common::{xcm_sender, ToAuthor}; use xcm::latest::prelude::*; +use xcm_executor::XcmExecutor; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, BackingToPlurality, diff --git a/runtime/polkadot/src/xcm_config.rs b/runtime/polkadot/src/xcm_config.rs index 7e5da2b2839c..ced0ea941f8a 100644 --- a/runtime/polkadot/src/xcm_config.rs +++ b/runtime/polkadot/src/xcm_config.rs @@ -34,6 +34,7 @@ use xcm_builder::{ IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; +use xcm_executor::XcmExecutor; parameter_types! { /// The location of the DOT token, from the context of this chain. Since this token is native to this diff --git a/runtime/rococo/src/xcm_config.rs b/runtime/rococo/src/xcm_config.rs index 3dc0f5ec9932..659072b54601 100644 --- a/runtime/rococo/src/xcm_config.rs +++ b/runtime/rococo/src/xcm_config.rs @@ -28,6 +28,7 @@ use frame_support::{ use runtime_common::{xcm_sender, ToAuthor}; use sp_std::prelude::*; use xcm::latest::prelude::*; +use xcm_executor::XcmExecutor; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, diff --git a/runtime/westend/src/tests.rs b/runtime/westend/src/tests.rs index f53f9ec54d91..758d64851ecf 100644 --- a/runtime/westend/src/tests.rs +++ b/runtime/westend/src/tests.rs @@ -17,7 +17,7 @@ //! Tests for the Westend Runtime Configuration use crate::*; -use xcm::latest::{AssetId::*, Fungibility::*, MultiLocation}; +use xcm::latest::prelude::*; #[test] fn remove_keys_weight_is_sensible() { diff --git a/runtime/westend/src/weights/xcm/mod.rs b/runtime/westend/src/weights/xcm/mod.rs index 0116b0a04d54..229cb7feb532 100644 --- a/runtime/westend/src/weights/xcm/mod.rs +++ b/runtime/westend/src/weights/xcm/mod.rs @@ -79,7 +79,7 @@ impl XcmWeightInfo for WestendXcmWeight { fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmBalancesWeight::::receive_teleported_asset()) } - fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> Weight { + fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64, _querier: &Option) -> Weight { XcmGeneric::::query_response() } fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight { @@ -212,4 +212,10 @@ impl XcmWeightInfo for WestendXcmWeight { fn clear_transact_status() -> Weight { XcmGeneric::::clear_transact_status() } + fn universal_origin(_: &Junction) -> Weight { + 10_000_000_000 + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + 10_000_000_000 + } } diff --git a/runtime/westend/src/xcm_config.rs b/runtime/westend/src/xcm_config.rs index 72d083121d9e..ef86485dad3e 100644 --- a/runtime/westend/src/xcm_config.rs +++ b/runtime/westend/src/xcm_config.rs @@ -34,6 +34,7 @@ use xcm_builder::{ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; +use xcm_executor::XcmExecutor; parameter_types! { pub const TokenLocation: MultiLocation = Here.into_location(); diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index eff8aa162a5d..315bec110b1a 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -161,13 +161,13 @@ fn custom_querier_works() { vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let querier: MultiLocation = - (Parent, AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }).into(); + (Parent, AccountId32 { network: None, id: ALICE.into() }).into(); let r = TestNotifier::prepare_new_query(Origin::signed(ALICE), querier.clone()); assert_eq!(r, Ok(())); let status = QueryStatus::Pending { responder: MultiLocation::from(AccountId32 { - network: AnyNetwork::get(), + network: None, id: ALICE.into(), }) .into(), @@ -179,7 +179,7 @@ fn custom_querier_works() { // Supplying no querier when one is expected will fail let r = XcmExecutor::::execute_xcm_in_credit( - AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(), + AccountId32 { network: None, id: ALICE.into() }, Xcm(vec![QueryResponse { query_id: 0, response: Response::ExecutionResult(None), @@ -193,7 +193,7 @@ fn custom_querier_works() { assert_eq!( last_event(), Event::XcmPallet(crate::Event::InvalidQuerier( - AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(), + AccountId32 { network: None, id: ALICE.into() }.into(), 0, querier.clone(), None, @@ -202,7 +202,7 @@ fn custom_querier_works() { // Supplying the wrong querier will also fail let r = XcmExecutor::::execute_xcm_in_credit( - AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(), + AccountId32 { network: None, id: ALICE.into() }, Xcm(vec![QueryResponse { query_id: 0, response: Response::ExecutionResult(None), @@ -216,7 +216,7 @@ fn custom_querier_works() { assert_eq!( last_event(), Event::XcmPallet(crate::Event::InvalidQuerier( - AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(), + AccountId32 { network: None, id: ALICE.into() }.into(), 0, querier.clone(), Some(MultiLocation::here()), @@ -225,7 +225,7 @@ fn custom_querier_works() { // Multiple failures should not have changed the query state let r = XcmExecutor::::execute_xcm( - AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(), + AccountId32 { network: None, id: ALICE.into() }, Xcm(vec![QueryResponse { query_id: 0, response: Response::ExecutionResult(None), diff --git a/xcm/src/v2/mod.rs b/xcm/src/v2/mod.rs index feaed5bffac4..3fa82fc43ec0 100644 --- a/xcm/src/v2/mod.rs +++ b/xcm/src/v2/mod.rs @@ -930,8 +930,8 @@ impl TryFrom> for Instruction { use NewInstruction::*; Ok(match instruction { WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), - ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets), - ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets), + ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), + ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), QueryResponse { query_id, response, max_weight, .. } => Self::QueryResponse { query_id, response: response.try_into()?, max_weight }, TransferAsset { assets, beneficiary } => Self::TransferAsset { diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index e8a1a7dfa9aa..e26c6118e2de 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -44,8 +44,7 @@ pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; pub use traits::{ - Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, XcmWeightInfo, - PreparedMessage, + Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, PreparedMessage, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; diff --git a/xcm/src/v3/multilocation.rs b/xcm/src/v3/multilocation.rs index 5588f9191c2f..33390f68792c 100644 --- a/xcm/src/v3/multilocation.rs +++ b/xcm/src/v3/multilocation.rs @@ -394,6 +394,17 @@ impl MultiLocation { Ok(()) } + /// Consume `self` and return a new value representing the same location from the point of view + /// of `target`. The context of `self` is provided as `ancestry`. + /// + /// Returns the original `self` in case of overflow. + pub fn reanchored(mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result { + match self.reanchor(target, ancestry) { + Ok(()) => Ok(self), + Err(()) => Err(self), + } + } + /// Treating `self` as a context, determine how it would be referenced by a `target` location. pub fn inverted(&self, target: &MultiLocation) -> Result { use Junction::OnlyChild; diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 23a702264347..f487751be119 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -405,82 +405,3 @@ impl SendXcm for Tuple { Err(SendError::CannotReachDestination(destination.into(), message)) } } - -/// The info needed to weigh an XCM. -// TODO: Automate Generation -pub trait XcmWeightInfo { - fn withdraw_asset(assets: &MultiAssets) -> Weight; - fn reserve_asset_deposited(assets: &MultiAssets) -> Weight; - fn receive_teleported_asset(assets: &MultiAssets) -> Weight; - fn query_response(query_id: &u64, response: &Response, max_weight: &u64) -> Weight; - fn transfer_asset(assets: &MultiAssets, beneficiary: &MultiLocation) -> Weight; - fn transfer_reserve_asset(assets: &MultiAssets, dest: &MultiLocation, xcm: &Xcm<()>) -> Weight; - fn transact( - origin_kind: &OriginKind, - require_weight_at_most: &u64, - call: &DoubleEncoded, - ) -> Weight; - fn hrmp_new_channel_open_request( - sender: &u32, - max_message_size: &u32, - max_capacity: &u32, - ) -> Weight; - fn hrmp_channel_accepted(recipient: &u32) -> Weight; - fn hrmp_channel_closing(initiator: &u32, sender: &u32, recipient: &u32) -> Weight; - fn clear_origin() -> Weight; - fn descend_origin(who: &InteriorMultiLocation) -> Weight; - fn report_error(response_info: &QueryResponseInfo) -> Weight; - fn deposit_asset(assets: &MultiAssetFilter, beneficiary: &MultiLocation) -> Weight; - fn deposit_reserve_asset( - assets: &MultiAssetFilter, - dest: &MultiLocation, - xcm: &Xcm<()>, - ) -> Weight; - fn exchange_asset(give: &MultiAssetFilter, receive: &MultiAssets) -> Weight; - fn initiate_reserve_withdraw( - assets: &MultiAssetFilter, - reserve: &MultiLocation, - xcm: &Xcm<()>, - ) -> Weight; - fn initiate_teleport(assets: &MultiAssetFilter, dest: &MultiLocation, xcm: &Xcm<()>) -> Weight; - fn report_holding(response_info: &QueryResponseInfo, assets: &MultiAssetFilter) -> Weight; - fn buy_execution(fees: &MultiAsset, weight_limit: &WeightLimit) -> Weight; - fn refund_surplus() -> Weight; - fn set_error_handler(xcm: &Xcm) -> Weight; - fn set_appendix(xcm: &Xcm) -> Weight; - fn clear_error() -> Weight; - fn claim_asset(assets: &MultiAssets, ticket: &MultiLocation) -> Weight; - fn trap(code: &u64) -> Weight; - fn subscribe_version(query_id: &QueryId, max_response_weight: &u64) -> Weight; - fn unsubscribe_version() -> Weight; - fn burn_asset(_assets: &MultiAssets) -> Weight { - 0 - } - fn expect_asset(_assets: &MultiAssets) -> Weight { - 0 - } - fn expect_origin(_origin: &Option) -> Weight { - 0 - } - fn expect_error(_error: &Option<(u32, Error)>) -> Weight { - 0 - } - fn query_pallet() -> Weight { - 0 - } - fn expect_pallet(_pallet_index: &u32) -> Weight { - 0 - } - fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { - 0 - } - fn clear_transact_status() -> Weight { - 0 - } - fn universal_origin(_: &Junction) -> Weight { - 0 - } - fn export_message(_: &NetworkId, _: &InteriorMultiLocation, _: &Xcm<()>) -> Weight { - 0 - } -} diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index d7189f540ac7..ae94a9693114 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -36,7 +36,6 @@ pub use sp_std::{ marker::PhantomData, }; pub use xcm::latest::prelude::*; -use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ traits::{ ConvertOrigin, ExportXcm, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset, diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 111df3a2dfe6..f335470c2801 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -662,7 +662,7 @@ fn prepaid_result_of_query_should_get_free_execution() { query_id, response: the_response.clone(), max_weight: 10, - querier: Some(Here.into().into()), + querier: Some(Here.into()), }]); let weight_limit = 10; diff --git a/xcm/xcm-builder/tests/scenarios.rs b/xcm/xcm-builder/tests/scenarios.rs index 2af2f6d74234..6932c006546a 100644 --- a/xcm/xcm-builder/tests/scenarios.rs +++ b/xcm/xcm-builder/tests/scenarios.rs @@ -142,7 +142,7 @@ fn report_holding_works() { query_id: response_info.query_id, response: Response::Assets(vec![].into()), max_weight: response_info.max_weight, - querier: Some(Here.into().into()), + querier: Some(Here.into()), }]), )] ); diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index cde0c146a279..ef4620d2a39a 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -601,7 +601,7 @@ impl XcmExecutor { Ok(match local_querier { None => None, Some(q) => Some( - q.reanchored(&destination, &Config::LocationInverter::ancestry()) + q.reanchored(&destination, &Config::LocationInverter::universal_location().into()) .map_err(|_| XcmError::ReanchorFailed)?, ), }) From 1723ae9e490480bef967db86e9e73ebaeedc7168 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 22 Jan 2022 18:46:00 +0100 Subject: [PATCH 21/57] Make build --- runtime/kusama/src/xcm_config.rs | 4 +-- xcm/xcm-builder/src/lib.rs | 48 +------------------------------- 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index 96a2336098a2..170d1fd9ccb3 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -78,7 +78,7 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter< CheckAccount, >; -/// The means that we convert an the XCM message origin location into a local dispatch origin. +/// The means that we convert the XCM message origin location into a local dispatch origin. type LocalOriginConverter = ( // A `Signed` origin of the sovereign account that the original location controls. SovereignSignedViaLocation, @@ -194,4 +194,4 @@ impl pallet_xcm::Config for Runtime { type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; -} \ No newline at end of file +} diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index a83da60d7b2d..c81217454ec9 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -268,53 +268,7 @@ mod universal_exports { Router::send_xcm(bridge, message) } } - - /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction - /// and sends it to a destination known to be able to handle it. - /// - /// No effort is made to make payment to the bridge for its services, so the bridge location - /// must have been configured with a barrier rule allowing unpaid execution for this message - /// coming from our origin. - /// - /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` - /// and `DescendOrigin` in order to ensure that the message is executed with our Origin. - pub struct UnpaidRemoteExporter< - Bridges, - Router, - Ancestry, - >(PhantomData<(Bridges, Router, Ancestry)>); - impl< - Bridges: ExporterFor, - Router: SendXcm, - Ancestry: Get, - > SendXcm for UnpaidRemoteExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let dest = dest.into(); - - // TODO: proper matching so we can be sure that it's the only viable send_xcm before we - // attempt and thus can acceptably consume dest & xcm. - let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); - - let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; - let (remote_network, remote_location, local_network, local_location) = devolved; - - let bridge = Bridges::exporter_for(&remote_network, &remote_location).ok_or(err)?; - - let mut inner_xcm: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); - inner_xcm.inner_mut().extend(xcm.into_iter()); - let message = Xcm(vec![ - ExportMessage { - network: remote_network, - destination: remote_location, - xcm: inner_xcm, - }, - ]); - Router::send_xcm(bridge, message) - } - } + /* /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction /// and sends it to a destination known to be able to handle it. From 30fbd39905f358f7ec95ce9ce06216c484d695ab Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 23 Jan 2022 18:41:54 +0000 Subject: [PATCH 22/57] WithComputedOrigin and SovereignPaidRemoteExporter --- xcm/pallet-xcm/src/tests.rs | 2 +- xcm/xcm-builder/src/barriers.rs | 124 ++++++++++-- xcm/xcm-builder/src/lib.rs | 185 +++++++++--------- xcm/xcm-builder/src/tests.rs | 16 +- xcm/xcm-executor/src/lib.rs | 2 +- xcm/xcm-executor/src/traits/should_execute.rs | 14 +- 6 files changed, 208 insertions(+), 135 deletions(-) diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 315bec110b1a..391104346345 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -721,7 +721,7 @@ fn basic_subscription_works() { ]); assert_ok!(AllowKnownQueryResponses::::should_execute( &remote, - &mut message, + message.inner_mut(), weight, &mut 0 )); diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index be4a6291c61d..dea3fea9a4ba 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -16,10 +16,10 @@ //! Various implementations for `ShouldExecute`. -use frame_support::{ensure, traits::Contains, weights::Weight}; +use frame_support::{ensure, traits::{Contains, Get}, weights::Weight}; use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; -use xcm::latest::{Instruction::*, Junction, Junctions, MultiLocation, WeightLimit::*, Xcm}; +use xcm::latest::{Instruction::{self, *}, Junction, Junctions, MultiLocation, WeightLimit::*, Xcm}; use xcm_executor::traits::{OnResponse, ShouldExecute}; /// Execution barrier that just takes `max_weight` from `weight_credit`. @@ -31,14 +31,14 @@ pub struct TakeWeightCredit; impl ShouldExecute for TakeWeightCredit { fn should_execute( _origin: &MultiLocation, - _message: &mut Xcm, + _instructions: &mut [Instruction], max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { log::trace!( target: "xcm::barriers", - "TakeWeightCredit origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", - _origin, _message, max_weight, weight_credit, + "TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", + _origin, _instructions, max_weight, weight_credit, ); *weight_credit = weight_credit.checked_sub(max_weight).ok_or(())?; Ok(()) @@ -54,17 +54,18 @@ pub struct AllowTopLevelPaidExecutionFrom(PhantomData); impl> ShouldExecute for AllowTopLevelPaidExecutionFrom { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + instructions: &mut [Instruction], max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { log::trace!( target: "xcm::barriers", - "AllowTopLevelPaidExecutionFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", - origin, message, max_weight, _weight_credit, + "AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, instructions, max_weight, _weight_credit, ); + ensure!(T::contains(origin), ()); - let mut iter = message.0.iter_mut(); + let mut iter = instructions.iter_mut(); let i = iter.next().ok_or(())?; match i { ReceiveTeleportedAsset(..) | @@ -91,20 +92,101 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro } } -/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`) without any payments. +/// A derivative barrier, which scans the first `MaxPrefixes` instructions for origin-alterers and +/// then evaluates `should_execute` of the `InnerBarrier` based on the remaining instructions and +/// the newly computed origin. +/// +/// This effectively allows for the possibility of distinguishing an origin which is acting as a +/// router for its derivative locations (or as a bridge for a remote location) and an origin which +/// is actually trying to send a message for itself. In the former case, the message will be +/// prefixed with origin-mutating instructions. +/// +/// Any barriers which should be interpreted based on the computed origin rather than the original +/// message origin should be subject to this. This is the case for most barriers since the +/// effective origin is generally more important than the routing origin. Any other barriers, and +/// especially those which should be interpreted only the routing origin should not be subject to +/// this. +/// +/// E.g. +/// ``` +/// type MyBarrier = ( +/// TakeWeightCredit, +/// AllowTopLevelPaidExecutionFrom, +/// WithComputedOrigin<( +/// AllowTopLevelPaidExecutionFrom, +/// AllowUnpaidExecutionFrom, +/// AllowSubscriptionsFrom, +/// AllowKnownQueryResponses, +/// )>, +/// ); +/// ``` +/// +/// In the above example, `AllowUnpaidExecutionFrom` appears once underneath +/// `WithComputedOrigin`. This is in order to distinguish between messages which are notionally +/// from a derivative location of `ParentLocation` but that just happened to be sent via +/// `ParentLocaction` rather than messages that were sent by the parent. +/// +/// Similarly `AllowTopLevelPaidExecutionFrom` appears twice: once inside of `WithComputedOrigin` +/// where we provide the list of origins which are derivative origins, and then secondly outside +/// of `WithComputedOrigin` where we provide the list of locations which are direct origins. It's +/// reasonable for these lists to be merged into one and that used both inside and out. +/// +/// Finally, we see `AllowSubscriptionsFrom` and `AllowKnownQueryResponses` are both inside of +/// `WithComputedOrigin`. This means that if a message begins with origin-mutating instructions, +/// then it must be the finally computed origin which we accept subscriptions or expect a query +/// response from. For example, even if an origin appeared in the `AllowedSubscribers` list, we +/// would ignore this rule if it began with origin mutators and they changed the origin to something +/// which was not on the list. +pub struct WithComputedOrigin(PhantomData<(InnerBarrier, MaxPrefixes)>); +impl< + InnerBarrier: ShouldExecute, + MaxPrefixes: Get, +> ShouldExecute for WithComputedOrigin { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + log::trace!( + target: "xcm::barriers", + "WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, instructions, max_weight, weight_credit, + ); + let mut actual_origin = origin.clone(); + let mut skipped = 0; + // NOTE: We do not check the validity of `UniversalOrigin` here, meaning that a malicious + // origin could place a `UniversalOrigin` in order to spoof some location which gets free + // execution. This technical could get it past the barrier condition, but the execution + // would instantly fail since the first instruction would cause an error with the + // invalid UniversalOrigin. + while skipped < MaxPrefixes::get() as usize { + match instructions.get(skipped) { + Some(UniversalOrigin(j)) => { actual_origin = j.clone().into(); }, + Some(DescendOrigin(j)) => { actual_origin.append_with(j.clone()).map_err(|_| ())?; }, + _ => break, + } + skipped += 1; + } + InnerBarrier::should_execute(&actual_origin, &mut instructions[skipped..], max_weight, weight_credit) + } +} + +/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`) without +/// any payments. /// Use only for executions from trusted origin groups. pub struct AllowUnpaidExecutionFrom(PhantomData); impl> ShouldExecute for AllowUnpaidExecutionFrom { fn should_execute( origin: &MultiLocation, - _message: &mut Xcm, + instructions: &mut [Instruction], _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { log::trace!( target: "xcm::barriers", - "AllowUnpaidExecutionFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", - origin, _message, _max_weight, _weight_credit, + "AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, instructions, _max_weight, _weight_credit, ); ensure!(T::contains(origin), ()); Ok(()) @@ -128,16 +210,16 @@ pub struct AllowKnownQueryResponses(PhantomData ShouldExecute for AllowKnownQueryResponses { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + instructions: &mut [Instruction], _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { log::trace!( target: "xcm::barriers", - "AllowKnownQueryResponses origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", - origin, message, _max_weight, _weight_credit, + "AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, instructions, _max_weight, _weight_credit, ); - match message.0.first() { + match instructions.first() { Some(QueryResponse { query_id, querier, .. }) if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) => Ok(()), @@ -152,17 +234,17 @@ pub struct AllowSubscriptionsFrom(PhantomData); impl> ShouldExecute for AllowSubscriptionsFrom { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + instructions: &mut [Instruction], _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { log::trace!( target: "xcm::barriers", - "AllowSubscriptionsFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", - origin, message, _max_weight, _weight_credit, + "AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", + origin, instructions, _max_weight, _weight_credit, ); ensure!(T::contains(origin), ()); - match (message.0.len(), message.0.first()) { + match (instructions.len(), instructions.first()) { (1, Some(SubscribeVersion { .. })) | (1, Some(UnsubscribeVersion)) => Ok(()), _ => Err(()), } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index c81217454ec9..d7d98d41f2c1 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -72,7 +72,7 @@ pub use filter_asset_location::{Case, NativeAsset}; mod universal_exports { use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; - use frame_support::traits::Get; + use frame_support::{traits::Get, ensure}; use xcm::prelude::*; use xcm_executor::traits::ExportXcm; @@ -129,6 +129,8 @@ mod universal_exports { /// /// No effort is made to charge for any bridge fees, so this can only be used when it is known /// that the message sending cannot be abused in any way. + /// + /// This is only useful when the local chain has bridging capabilities. pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancestry)>); impl> SendXcm for LocalUnpaidExporter { fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { @@ -146,68 +148,33 @@ mod universal_exports { } } - /// Alternative implentation of `LocalUnpaidExporter` which uses the `ExportMessage` - /// instruction executing locally. - pub struct LocalUnpaidExecutingExporter< - Executer, - Ancestry, - WeightLimit, - Call, - >(PhantomData<(Executer, Ancestry, WeightLimit, Call)>); - impl< - Executer: ExecuteXcm, - Ancestry: Get, - WeightLimit: Get, - Call, - > SendXcm for LocalUnpaidExecutingExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let dest = dest.into(); - - // TODO: proper matching so we can be sure that it's the only viable send_xcm before we - // attempt and thus can acceptably consume dest & xcm. - let err = Err(SendError::CannotReachDestination(dest.clone(), xcm.clone())); - - let devolved = match ensure_is_remote(Ancestry::get(), dest) { - Ok(x) => x, - Err(_) => return err, - }; - let (remote_network, remote_location, local_network, local_location) = devolved; - - let mut inner_xcm: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); - inner_xcm.inner_mut().extend(xcm.into_iter()); + // TODO: `SendXcm` should include a price/weight calculator for calling prior to `send_xcm`. - let message = Xcm(vec![ ExportMessage { - network: remote_network, - destination: remote_location, - xcm: inner_xcm, - } ]); - let pre = match Executer::prepare(message) { - Ok(x) => x, - Err(_) => return err, - }; - // We just swallow the weight - it should be constant. - let weight_credit = pre.weight_of(); - match Executer::execute(Here, pre, weight_credit) { - Outcome::Complete(_) => Ok(()), - _ => return err, - } - } - } - - // TODO: `LocalPaidExecutingExporter` able to accept from non-`Here`, but local, origins. + // TODO: Payment Barrier should ignore prefix of `UniversalOrigin`, `DescendOrigin`. + // TODO: Create `BridgedOrigin` Barrier which understands prefix of `UniversalOrigin`, `DescendOrigin`. + // TODO: Check and add/alter other barriers. pub trait ExporterFor { - fn exporter_for(network: &NetworkId, remote_location: &InteriorMultiLocation) -> Option; + /// Return the locally-routable bridge (if any) capable of forwarding `message` to the + /// `remote_location` on the remote `network`, together with the payment which is required. + /// + /// The payment is specified from the context of the bridge, not the local chain. + fn exporter_for( + network: &NetworkId, + remote_location: &InteriorMultiLocation, + message: &Xcm<()>, + ) -> Option<(MultiLocation, Option)>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl ExporterFor for Tuple { - fn exporter_for(network: &NetworkId, remote_location: &InteriorMultiLocation) -> Option { + fn exporter_for( + network: &NetworkId, + remote_location: &InteriorMultiLocation, + message: &Xcm<()>, + ) -> Option<(MultiLocation, Option)> { for_tuples!( #( - if let Some(r) = Tuple::exporter_for(network, remote_location) { + if let Some(r) = Tuple::exporter_for(network, remote_location, message) { return Some(r); } )* ); @@ -216,21 +183,28 @@ mod universal_exports { } pub struct NetworkExportTable(sp_std::marker::PhantomData); - impl> ExporterFor for NetworkExportTable { - fn exporter_for(network: &NetworkId, _: &InteriorMultiLocation) -> Option { - T::get().iter().find(|(ref j, _)| j == network).map(|(_, l)| l.clone()) + impl)]>> ExporterFor for NetworkExportTable { + fn exporter_for( + network: &NetworkId, + _: &InteriorMultiLocation, + _: &Xcm<()>, + ) -> Option<(MultiLocation, Option)> { + T::get().iter().find(|(ref j, ..)| j == network).map(|(_, l, p)| (l.clone(), p.clone())) } } /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction /// and sends it to a destination known to be able to handle it. /// + /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` + /// and `DescendOrigin` in order to ensure that the message is executed with our Origin. + /// /// No effort is made to make payment to the bridge for its services, so the bridge location /// must have been configured with a barrier rule allowing unpaid execution for this message /// coming from our origin. /// - /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` - /// and `DescendOrigin` in order to ensure that the message is executed with our Origin. + /// This is only useful if we have special dispensation by the remote bridges to have the + /// `ExportMessage` instruction executed without payment. pub struct UnpaidRemoteExporter< Bridges, Router, @@ -251,13 +225,23 @@ mod universal_exports { let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; let (remote_network, remote_location, local_network, local_location) = devolved; - let bridge = Bridges::exporter_for(&remote_network, &remote_location).ok_or(err)?; - + // Prepend the desired message with instructions which effectively rewrite the origin. + // + // This only works because the remote chain empowers the bridge + // to speak for the local network. let mut inner_xcm: Xcm<()> = vec![ UniversalOrigin(GlobalConsensus(local_network)), DescendOrigin(local_location), ].into(); inner_xcm.inner_mut().extend(xcm.into_iter()); + + let (bridge, payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) + .ok_or(err.clone())?; + ensure!(payment.is_none(), err); + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. This will only work if the bridge will do the message + // export for free. Common-good chains will typically be afforded this. let message = Xcm(vec![ ExportMessage { network: remote_network, @@ -268,64 +252,71 @@ mod universal_exports { Router::send_xcm(bridge, message) } } - -/* + /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction /// and sends it to a destination known to be able to handle it. /// - /// No effort is made to make payment to the bridge for its services, so the bridge location - /// must have been configured with a barrier rule allowing this message unpaid execution. - /// /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` /// and `DescendOrigin` in order to ensure that the message is executed with this Origin. - pub struct PaidRemoteExporter< - Executer, + /// + /// The `ExportMessage` instruction on the bridge is paid for from the local chain's sovereign + /// account on the bridge. The amount paid is determined through the `ExporterFor` trait. + pub struct SovereignPaidRemoteExporter< + Bridges, + Router, Ancestry, - WeightLimit, - Call, - >(PhantomData<(Executer, Ancestry, WeightLimit, Call)>); + >(PhantomData<(Bridges, Router, Ancestry)>); impl< - Executer: ExecuteXcm, + Bridges: ExporterFor, + Router: SendXcm, Ancestry: Get, - WeightLimit: Get, - Call, - > SendXcm for LocalPaidExecutingExporter { + > SendXcm for SovereignPaidRemoteExporter { fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { let dest = dest.into(); // TODO: proper matching so we can be sure that it's the only viable send_xcm before we // attempt and thus can acceptably consume dest & xcm. - let err = Err(SendError::CannotReachDestination(dest.clone(), xcm.clone())); + let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); - let devolved = match ensure_is_remote(Ancestry::get(), dest) { - Ok(x) => x, - Err(_) => return err, - }; + let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; let (remote_network, remote_location, local_network, local_location) = devolved; + // Prepend the desired message with instructions which effectively rewrite the origin. + // + // This only works because the remote chain empowers the bridge + // to speak for the local network. let mut inner_xcm: Xcm<()> = vec![ UniversalOrigin(GlobalConsensus(local_network)), DescendOrigin(local_location), ].into(); inner_xcm.inner_mut().extend(xcm.into_iter()); - let message = Xcm(vec![ - WithdrawAsset((Here, ).into()), - BuyExecution { fees: Wild(AllCounted(1)), weight_limit: Unlimited }, - DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, - ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }, - ]); - let pre = match Executer::prepare(message) { - Ok(x) => x, - Err(_) => return err, + let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) + .ok_or(err.clone())?; + let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge) + .map_err(|_| err.clone())?; + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. This will only work if the bridge will do the message + // export for free. Common-good chains will typically be afforded this. + let export_instruction = ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, }; - // We just swallow the weight - it should be constant. - let weight_credit = pre.weight_of(); - match Executer::execute(Here, pre, weight_credit) { - Outcome::Complete(_) => Ok(()), - _ => return err, - } + + let message = Xcm(if let Some(payment) = maybe_payment { + vec![ + WithdrawAsset(payment.clone().into()), + BuyExecution { fees: payment, weight_limit: Unlimited }, + export_instruction, + RefundSurplus, + DepositAsset { assets: All.into(), beneficiary: local_from_bridge }, + ] + } else { + vec![export_instruction] + }); + Router::send_xcm(bridge, message) } } -*/ } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index f335470c2801..2ab8ebe24530 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -56,11 +56,11 @@ fn take_weight_credit_barrier_should_work() { let mut message = Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]); let mut weight_credit = 10; - let r = TakeWeightCredit::should_execute(&Parent.into(), &mut message, 10, &mut weight_credit); + let r = TakeWeightCredit::should_execute(&Parent.into(), message.inner_mut(), 10, &mut weight_credit); assert_eq!(r, Ok(())); assert_eq!(weight_credit, 0); - let r = TakeWeightCredit::should_execute(&Parent.into(), &mut message, 10, &mut weight_credit); + let r = TakeWeightCredit::should_execute(&Parent.into(), message.inner_mut(), 10, &mut weight_credit); assert_eq!(r, Err(())); assert_eq!(weight_credit, 0); } @@ -74,7 +74,7 @@ fn allow_unpaid_should_work() { let r = AllowUnpaidExecutionFrom::>::should_execute( &Parachain(1).into(), - &mut message, + message.inner_mut(), 10, &mut 0, ); @@ -82,7 +82,7 @@ fn allow_unpaid_should_work() { let r = AllowUnpaidExecutionFrom::>::should_execute( &Parent.into(), - &mut message, + message.inner_mut(), 10, &mut 0, ); @@ -98,7 +98,7 @@ fn allow_paid_should_work() { let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &Parachain(1).into(), - &mut message, + message.inner_mut(), 10, &mut 0, ); @@ -113,7 +113,7 @@ fn allow_paid_should_work() { let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &Parent.into(), - &mut underpaying_message, + underpaying_message.inner_mut(), 30, &mut 0, ); @@ -128,7 +128,7 @@ fn allow_paid_should_work() { let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &Parachain(1).into(), - &mut paying_message, + paying_message.inner_mut(), 30, &mut 0, ); @@ -136,7 +136,7 @@ fn allow_paid_should_work() { let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &Parent.into(), - &mut paying_message, + paying_message.inner_mut(), 30, &mut 0, ); diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 07aa04f44c68..f8d4c9095321 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -94,7 +94,7 @@ impl ExecuteXcm for XcmExecutor { weight_credit, ); if let Err(e) = - Config::Barrier::should_execute(&origin, &mut message, xcm_weight, &mut weight_credit) + Config::Barrier::should_execute(&origin, message.inner_mut(), xcm_weight, &mut weight_credit) { log::debug!( target: "xcm::execute_xcm_in_credit", diff --git a/xcm/xcm-executor/src/traits/should_execute.rs b/xcm/xcm-executor/src/traits/should_execute.rs index 5f94db0066b4..d3602c3da2de 100644 --- a/xcm/xcm-executor/src/traits/should_execute.rs +++ b/xcm/xcm-executor/src/traits/should_execute.rs @@ -16,7 +16,7 @@ use frame_support::weights::Weight; use sp_std::result::Result; -use xcm::latest::{MultiLocation, Xcm}; +use xcm::latest::{MultiLocation, Instruction}; /// Trait to determine whether the execution engine should actually execute a given XCM. /// @@ -26,14 +26,14 @@ pub trait ShouldExecute { /// Returns `true` if the given `message` may be executed. /// /// - `origin`: The origin (sender) of the message. - /// - `message`: The message itself. + /// - `instructions`: The message itself. /// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message. /// - `weight_credit`: The pre-established amount of weight that the system has determined this /// message may utilize in its execution. Typically non-zero only because of prior fee /// payment, but could in principle be due to other factors. fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + instructions: &mut [Instruction], max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()>; @@ -43,21 +43,21 @@ pub trait ShouldExecute { impl ShouldExecute for Tuple { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + instructions: &mut [Instruction], max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { for_tuples!( #( - match Tuple::should_execute(origin, message, max_weight, weight_credit) { + match Tuple::should_execute(origin, instructions, max_weight, weight_credit) { Ok(()) => return Ok(()), _ => (), } )* ); log::trace!( target: "xcm::should_execute", - "did not pass barrier: origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}", + "did not pass barrier: origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", origin, - message, + instructions, max_weight, weight_credit, ); From f75c343fa050a87f995ba73a2efa29efe9b16728 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 28 Jan 2022 19:40:19 +0000 Subject: [PATCH 23/57] Remove TODOs --- xcm/xcm-builder/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index d7d98d41f2c1..29291fee2498 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -150,10 +150,6 @@ mod universal_exports { // TODO: `SendXcm` should include a price/weight calculator for calling prior to `send_xcm`. - // TODO: Payment Barrier should ignore prefix of `UniversalOrigin`, `DescendOrigin`. - // TODO: Create `BridgedOrigin` Barrier which understands prefix of `UniversalOrigin`, `DescendOrigin`. - // TODO: Check and add/alter other barriers. - pub trait ExporterFor { /// Return the locally-routable bridge (if any) capable of forwarding `message` to the /// `remote_location` on the remote `network`, together with the payment which is required. From 8af0a3ae604e9d8aa07fbc127d0fbad75b19969f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 31 Jan 2022 17:14:40 +0000 Subject: [PATCH 24/57] Slim bridge API and tests. --- xcm/src/lib.rs | 58 +++- xcm/src/v3/junctions.rs | 126 ++++++- xcm/xcm-builder/src/barriers.rs | 2 +- xcm/xcm-builder/src/lib.rs | 251 +------------- xcm/xcm-builder/src/universal_exports.rs | 397 ++++++++++++++++++++++ xcm/xcm-executor/src/traits/conversion.rs | 2 +- 6 files changed, 576 insertions(+), 260 deletions(-) create mode 100644 xcm/xcm-builder/src/universal_exports.rs diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 1cb369199a9b..a7360bb8cd51 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -146,6 +146,60 @@ impl TryFrom for v3::MultiLocation { } } +/// A single `InteriorMultiLocation` value, together with its version code. +#[derive(Derivative, Encode, Decode, TypeInfo)] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] +#[codec(encode_bound())] +#[codec(decode_bound())] +pub enum VersionedInteriorMultiLocation { + V1(v1::InteriorMultiLocation), + V3(v3::InteriorMultiLocation), +} + +impl IntoVersion for VersionedInteriorMultiLocation { + fn into_version(self, n: Version) -> Result { + Ok(match n { + 1 | 2 => Self::V1(self.try_into()?), + 3 => Self::V3(self.try_into()?), + _ => return Err(()), + }) + } +} + +impl From for VersionedInteriorMultiLocation { + fn from(x: v1::InteriorMultiLocation) -> Self { + VersionedInteriorMultiLocation::V1(x) + } +} + +impl> From for VersionedInteriorMultiLocation { + fn from(x: T) -> Self { + VersionedInteriorMultiLocation::V3(x.into()) + } +} + +impl TryFrom for v1::InteriorMultiLocation { + type Error = (); + fn try_from(x: VersionedInteriorMultiLocation) -> Result { + use VersionedInteriorMultiLocation::*; + match x { + V1(x) => Ok(x), + V3(x) => x.try_into(), + } + } +} + +impl TryFrom for v3::InteriorMultiLocation { + type Error = (); + fn try_from(x: VersionedInteriorMultiLocation) -> Result { + use VersionedInteriorMultiLocation::*; + match x { + V1(x) => x.try_into(), + V3(x) => Ok(x), + } + } +} + /// A single `Response` value, together with its version code. #[derive(Derivative, Encode, Decode, TypeInfo)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] @@ -548,8 +602,8 @@ pub type AlwaysRelease = AlwaysV2; pub mod prelude { pub use super::{ latest::prelude::*, AlwaysLatest, AlwaysRelease, AlwaysV2, AlwaysV3, IntoVersion, - Unsupported, Version as XcmVersion, VersionedMultiAsset, VersionedMultiAssets, - VersionedMultiLocation, VersionedResponse, VersionedXcm, WrapVersion, + Unsupported, Version as XcmVersion, VersionedInteriorMultiLocation, VersionedMultiAsset, + VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WrapVersion, }; } diff --git a/xcm/src/v3/junctions.rs b/xcm/src/v3/junctions.rs index 2302f404acd9..1b0f71926c3e 100644 --- a/xcm/src/v3/junctions.rs +++ b/xcm/src/v3/junctions.rs @@ -16,7 +16,7 @@ //! XCM `Junctions`/`InteriorMultiLocation` datatype. -use super::{Junction, MultiLocation}; +use super::{Junction, MultiLocation, NetworkId}; use core::{convert::TryFrom, mem, result}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -129,6 +129,32 @@ impl Junctions { MultiLocation { parents: n, interior: self } } + /// Extract the network ID treating this value as a universal location. + /// + /// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate + /// that this value is not a universal location. + pub fn global_consensus(&self) -> Result { + if let Some(Junction::GlobalConsensus(ref network)) = self.first() { + Ok(network.clone()) + } else { + Err(()) + } + } + + /// Consumes `self` and returns how `viewer` would address it locally. + pub fn relative_to(mut self, viewer: &Junctions) -> MultiLocation { + let mut i = 0; + while match (self.first(), viewer.at(i)) { (Some(x), Some(y)) => x == y, _ => false } { + self = self.split_first().0; + // NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times. + i += 1; + } + // AUDIT NOTES: + // - above loop ensures that `i <= viewer.len()`. + // - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`. + MultiLocation { parents: (viewer.len() - i) as u8, interior: self } + } + /// Returns first junction, or `None` if the location is empty. pub fn first(&self) -> Option<&Junction> { match &self { @@ -479,13 +505,93 @@ impl From<()> for Junctions { xcm_procedural::impl_conversion_functions_for_junctions_v3!(); -#[test] -fn test_conversion() { - use super::{Junction::*, Junctions::*, NetworkId::*}; - let x: Junctions = GlobalConsensus(Polkadot).into(); - assert_eq!(x, X1(GlobalConsensus(Polkadot))); - let x: Junctions = Polkadot.into(); - assert_eq!(x, X1(GlobalConsensus(Polkadot))); - let x: Junctions = (Polkadot, Kusama).into(); - assert_eq!(x, X2(GlobalConsensus(Polkadot), GlobalConsensus(Kusama))); +#[cfg(test)] +mod tests { + use alloc::vec; + use super::*; + use super::super::prelude::*; + + #[test] + fn relative_to_works() { + use Junctions::*; + use NetworkId::*; + assert_eq!(X1(Polkadot.into()).relative_to(&X1(Kusama.into())), (Parent, Polkadot).into()); + let base = X3(Kusama.into(), Parachain(1), PalletInstance(1)); + + // Ancestors. + assert_eq!( + Here.relative_to(&base), + (Parent, Parent, Parent).into() + ); + assert_eq!( + X1(Kusama.into()).relative_to(&base), + (Parent, Parent).into() + ); + assert_eq!( + X2(Kusama.into(), Parachain(1)).relative_to(&base), + (Parent,).into() + ); + assert_eq!( + X3(Kusama.into(), Parachain(1), PalletInstance(1)).relative_to(&base), + Here.into() + ); + + // Ancestors with one child. + assert_eq!( + X1(Polkadot.into()).relative_to(&base), + (Parent, Parent, Parent, Polkadot).into() + ); + assert_eq!( + X2(Kusama.into(), Parachain(2)).relative_to(&base), + (Parent, Parent, Parachain(2)).into() + ); + assert_eq!( + X3(Kusama.into(), Parachain(1), PalletInstance(2)).relative_to(&base), + (Parent, PalletInstance(2)).into() + ); + assert_eq!( + X4(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into()).relative_to(&base), + ([1u8; 32],).into() + ); + + // Ancestors with grandchildren. + assert_eq!( + X2(Polkadot.into(), Parachain(1)).relative_to(&base), + (Parent, Parent, Parent, Polkadot, Parachain(1)).into() + ); + assert_eq!( + X3(Kusama.into(), Parachain(2), PalletInstance(1)).relative_to(&base), + (Parent, Parent, Parachain(2), PalletInstance(1)).into() + ); + assert_eq!( + X4(Kusama.into(), Parachain(1), PalletInstance(2), [1u8; 32].into()).relative_to(&base), + (Parent, PalletInstance(2), [1u8; 32]).into() + ); + assert_eq!( + X5(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into(), vec![1].into()).relative_to(&base), + ([1u8; 32], vec![1]).into() + ); + } + + #[test] + fn global_consensus_works() { + use Junctions::*; + use NetworkId::*; + assert_eq!(X1(Polkadot.into()).global_consensus(), Ok(Polkadot)); + assert_eq!(X2(Kusama.into(), 1u64.into()).global_consensus(), Ok(Kusama)); + assert_eq!(Here.global_consensus(), Err(())); + assert_eq!(X1(1u64.into()).global_consensus(), Err(())); + assert_eq!(X2(1u64.into(), Kusama.into()).global_consensus(), Err(())); + } + + #[test] + fn test_conversion() { + use super::{Junction::*, Junctions::*, NetworkId::*}; + let x: Junctions = GlobalConsensus(Polkadot).into(); + assert_eq!(x, X1(GlobalConsensus(Polkadot))); + let x: Junctions = Polkadot.into(); + assert_eq!(x, X1(GlobalConsensus(Polkadot))); + let x: Junctions = (Polkadot, Kusama).into(); + assert_eq!(x, X2(GlobalConsensus(Polkadot), GlobalConsensus(Kusama))); + } } diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index dea3fea9a4ba..b0548465c1fd 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -19,7 +19,7 @@ use frame_support::{ensure, traits::{Contains, Get}, weights::Weight}; use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; -use xcm::latest::{Instruction::{self, *}, Junction, Junctions, MultiLocation, WeightLimit::*, Xcm}; +use xcm::latest::{Instruction::{self, *}, Junction, Junctions, MultiLocation, WeightLimit::*}; use xcm_executor::traits::{OnResponse, ShouldExecute}; /// Execution barrier that just takes `max_weight` from `weight_credit`. diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 29291fee2498..9a4945975745 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -70,249 +70,8 @@ pub use matches_fungible::{IsAbstract, IsConcrete}; mod filter_asset_location; pub use filter_asset_location::{Case, NativeAsset}; -mod universal_exports { - use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; - use frame_support::{traits::Get, ensure}; - use xcm::prelude::*; - use xcm_executor::traits::ExportXcm; - - fn ensure_is_remote( - universal_local: impl Into, - dest: impl Into, - ) -> Result<(NetworkId, InteriorMultiLocation, NetworkId, InteriorMultiLocation), MultiLocation> { - let dest = dest.into(); - let universal_local = universal_local.into(); - let (local_net, local_loc) = match universal_local.clone().split_first() { - (location, Some(GlobalConsensus(network))) => (network, location), - _ => return Err(dest), - }; - let universal_destination: InteriorMultiLocation = universal_local - .into_location() - .appended_with(dest.clone()) - .map_err(|x| x.1)? - .try_into()?; - let (remote_dest, remote_net) = match universal_destination.split_first() { - (d, Some(GlobalConsensus(n))) if n != local_net => (d, n), - _ => return Err(dest), - }; - Ok((remote_net, remote_dest, local_net, local_loc)) - } - - #[test] - fn ensure_is_remote_works() { - // A Kusama parachain is remote from the Polkadot Relay. - let x = ensure_is_remote(Polkadot, (Parent, Kusama, Parachain(1000))); - assert_eq!(x, Ok((Kusama, Parachain(1000).into(), Polkadot, Here))); - - // Polkadot Relay is remote from a Kusama parachain. - let x = ensure_is_remote((Kusama, Parachain(1000)), (Parent, Parent, Polkadot)); - assert_eq!(x, Ok((Polkadot, Here, Kusama, Parachain(1000).into()))); - - // Our own parachain is local. - let x = ensure_is_remote(Polkadot, Parachain(1000)); - assert_eq!(x, Err(Parachain(1000).into())); - - // Polkadot's parachain is not remote if we are Polkadot. - let x = ensure_is_remote(Polkadot, (Parent, Polkadot, Parachain(1000))); - assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); - - // If we don't have a consensus ancestor, then we cannot determine remoteness. - let x = ensure_is_remote((), (Parent, Polkadot, Parachain(1000))); - assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); - } - - /// Implementation of `SendXcm` which uses the given `ExportXcm` impl in order to forward the - /// message over a bridge. - /// - /// The actual message forwarded over the bridge is prepended with `UniversalOrigin` and - /// `DescendOrigin` in order to ensure that the message is executed with this Origin. - /// - /// No effort is made to charge for any bridge fees, so this can only be used when it is known - /// that the message sending cannot be abused in any way. - /// - /// This is only useful when the local chain has bridging capabilities. - pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancestry)>); - impl> SendXcm for LocalUnpaidExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let devolved = match ensure_is_remote(Ancestry::get(), dest) { - Ok(x) => x, - Err(dest) => return Err(SendError::CannotReachDestination(dest, xcm)), - }; - let (network, destination, local_network, local_location) = devolved; - let mut message: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); - message.inner_mut().extend(xcm.into_iter()); - Exporter::export_xcm(network, 0, destination, message) - } - } - - // TODO: `SendXcm` should include a price/weight calculator for calling prior to `send_xcm`. - - pub trait ExporterFor { - /// Return the locally-routable bridge (if any) capable of forwarding `message` to the - /// `remote_location` on the remote `network`, together with the payment which is required. - /// - /// The payment is specified from the context of the bridge, not the local chain. - fn exporter_for( - network: &NetworkId, - remote_location: &InteriorMultiLocation, - message: &Xcm<()>, - ) -> Option<(MultiLocation, Option)>; - } - - #[impl_trait_for_tuples::impl_for_tuples(30)] - impl ExporterFor for Tuple { - fn exporter_for( - network: &NetworkId, - remote_location: &InteriorMultiLocation, - message: &Xcm<()>, - ) -> Option<(MultiLocation, Option)> { - for_tuples!( #( - if let Some(r) = Tuple::exporter_for(network, remote_location, message) { - return Some(r); - } - )* ); - None - } - } - - pub struct NetworkExportTable(sp_std::marker::PhantomData); - impl)]>> ExporterFor for NetworkExportTable { - fn exporter_for( - network: &NetworkId, - _: &InteriorMultiLocation, - _: &Xcm<()>, - ) -> Option<(MultiLocation, Option)> { - T::get().iter().find(|(ref j, ..)| j == network).map(|(_, l, p)| (l.clone(), p.clone())) - } - } - - /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction - /// and sends it to a destination known to be able to handle it. - /// - /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` - /// and `DescendOrigin` in order to ensure that the message is executed with our Origin. - /// - /// No effort is made to make payment to the bridge for its services, so the bridge location - /// must have been configured with a barrier rule allowing unpaid execution for this message - /// coming from our origin. - /// - /// This is only useful if we have special dispensation by the remote bridges to have the - /// `ExportMessage` instruction executed without payment. - pub struct UnpaidRemoteExporter< - Bridges, - Router, - Ancestry, - >(PhantomData<(Bridges, Router, Ancestry)>); - impl< - Bridges: ExporterFor, - Router: SendXcm, - Ancestry: Get, - > SendXcm for UnpaidRemoteExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let dest = dest.into(); - - // TODO: proper matching so we can be sure that it's the only viable send_xcm before we - // attempt and thus can acceptably consume dest & xcm. - let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); - - let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; - let (remote_network, remote_location, local_network, local_location) = devolved; - - // Prepend the desired message with instructions which effectively rewrite the origin. - // - // This only works because the remote chain empowers the bridge - // to speak for the local network. - let mut inner_xcm: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); - inner_xcm.inner_mut().extend(xcm.into_iter()); - - let (bridge, payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) - .ok_or(err.clone())?; - ensure!(payment.is_none(), err); - - // We then send a normal message to the bridge asking it to export the prepended - // message to the remote chain. This will only work if the bridge will do the message - // export for free. Common-good chains will typically be afforded this. - let message = Xcm(vec![ - ExportMessage { - network: remote_network, - destination: remote_location, - xcm: inner_xcm, - }, - ]); - Router::send_xcm(bridge, message) - } - } - - /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction - /// and sends it to a destination known to be able to handle it. - /// - /// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` - /// and `DescendOrigin` in order to ensure that the message is executed with this Origin. - /// - /// The `ExportMessage` instruction on the bridge is paid for from the local chain's sovereign - /// account on the bridge. The amount paid is determined through the `ExporterFor` trait. - pub struct SovereignPaidRemoteExporter< - Bridges, - Router, - Ancestry, - >(PhantomData<(Bridges, Router, Ancestry)>); - impl< - Bridges: ExporterFor, - Router: SendXcm, - Ancestry: Get, - > SendXcm for SovereignPaidRemoteExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let dest = dest.into(); - - // TODO: proper matching so we can be sure that it's the only viable send_xcm before we - // attempt and thus can acceptably consume dest & xcm. - let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); - - let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; - let (remote_network, remote_location, local_network, local_location) = devolved; - - // Prepend the desired message with instructions which effectively rewrite the origin. - // - // This only works because the remote chain empowers the bridge - // to speak for the local network. - let mut inner_xcm: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); - inner_xcm.inner_mut().extend(xcm.into_iter()); - - let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) - .ok_or(err.clone())?; - let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge) - .map_err(|_| err.clone())?; - - // We then send a normal message to the bridge asking it to export the prepended - // message to the remote chain. This will only work if the bridge will do the message - // export for free. Common-good chains will typically be afforded this. - let export_instruction = ExportMessage { - network: remote_network, - destination: remote_location, - xcm: inner_xcm, - }; - - let message = Xcm(if let Some(payment) = maybe_payment { - vec![ - WithdrawAsset(payment.clone().into()), - BuyExecution { fees: payment, weight_limit: Unlimited }, - export_instruction, - RefundSurplus, - DepositAsset { assets: All.into(), beneficiary: local_from_bridge }, - ] - } else { - vec![export_instruction] - }); - Router::send_xcm(bridge, message) - } - } -} +mod universal_exports; +pub use universal_exports::{ + LocalUnpaidExporter, ExporterFor, NetworkExportTable, UnpaidRemoteExporter, + SovereignPaidRemoteExporter, +}; diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs new file mode 100644 index 000000000000..c3f15d647a19 --- /dev/null +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -0,0 +1,397 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; +use parity_scale_codec::{Encode, Decode}; +use frame_support::{traits::Get, ensure}; +use xcm::prelude::*; +use xcm_executor::traits::ExportXcm; + +fn ensure_is_remote( + universal_local: impl Into, + dest: impl Into, +) -> Result<(NetworkId, InteriorMultiLocation, NetworkId, InteriorMultiLocation), MultiLocation> { + let dest = dest.into(); + let universal_local = universal_local.into(); + let (local_net, local_loc) = match universal_local.clone().split_first() { + (location, Some(GlobalConsensus(network))) => (network, location), + _ => return Err(dest), + }; + let universal_destination: InteriorMultiLocation = universal_local + .into_location() + .appended_with(dest.clone()) + .map_err(|x| x.1)? + .try_into()?; + let (remote_dest, remote_net) = match universal_destination.split_first() { + (d, Some(GlobalConsensus(n))) if n != local_net => (d, n), + _ => return Err(dest), + }; + Ok((remote_net, remote_dest, local_net, local_loc)) +} + +/// Implementation of `SendXcm` which uses the given `ExportXcm` impl in order to forward the +/// message over a bridge. +/// +/// The actual message forwarded over the bridge is prepended with `UniversalOrigin` and +/// `DescendOrigin` in order to ensure that the message is executed with this Origin. +/// +/// No effort is made to charge for any bridge fees, so this can only be used when it is known +/// that the message sending cannot be abused in any way. +/// +/// This is only useful when the local chain has bridging capabilities. +pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancestry)>); +impl> SendXcm for LocalUnpaidExporter { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let devolved = match ensure_is_remote(Ancestry::get(), dest) { + Ok(x) => x, + Err(dest) => return Err(SendError::CannotReachDestination(dest, xcm)), + }; + let (network, destination, local_network, local_location) = devolved; + let mut message: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + message.inner_mut().extend(xcm.into_iter()); + Exporter::export_xcm(network, 0, destination, message) + } +} + +// TODO: `SendXcm` should include a price/weight calculator for calling prior to `send_xcm`. + +pub trait ExporterFor { + /// Return the locally-routable bridge (if any) capable of forwarding `message` to the + /// `remote_location` on the remote `network`, together with the payment which is required. + /// + /// The payment is specified from the context of the bridge, not the local chain. + fn exporter_for( + network: &NetworkId, + remote_location: &InteriorMultiLocation, + message: &Xcm<()>, + ) -> Option<(MultiLocation, Option)>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl ExporterFor for Tuple { + fn exporter_for( + network: &NetworkId, + remote_location: &InteriorMultiLocation, + message: &Xcm<()>, + ) -> Option<(MultiLocation, Option)> { + for_tuples!( #( + if let Some(r) = Tuple::exporter_for(network, remote_location, message) { + return Some(r); + } + )* ); + None + } +} + +pub struct NetworkExportTable(sp_std::marker::PhantomData); +impl)]>> ExporterFor for NetworkExportTable { + fn exporter_for( + network: &NetworkId, + _: &InteriorMultiLocation, + _: &Xcm<()>, + ) -> Option<(MultiLocation, Option)> { + T::get().iter().find(|(ref j, ..)| j == network).map(|(_, l, p)| (l.clone(), p.clone())) + } +} + +/// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction +/// and sends it to a destination known to be able to handle it. +/// +/// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` +/// and `DescendOrigin` in order to ensure that the message is executed with our Origin. +/// +/// No effort is made to make payment to the bridge for its services, so the bridge location +/// must have been configured with a barrier rule allowing unpaid execution for this message +/// coming from our origin. +/// +/// This is only useful if we have special dispensation by the remote bridges to have the +/// `ExportMessage` instruction executed without payment. +pub struct UnpaidRemoteExporter< + Bridges, + Router, + Ancestry, +>(PhantomData<(Bridges, Router, Ancestry)>); +impl< + Bridges: ExporterFor, + Router: SendXcm, + Ancestry: Get, +> SendXcm for UnpaidRemoteExporter { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let dest = dest.into(); + + // TODO: proper matching so we can be sure that it's the only viable send_xcm before we + // attempt and thus can acceptably consume dest & xcm. + let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); + + let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; + let (remote_network, remote_location, local_network, local_location) = devolved; + + // Prepend the desired message with instructions which effectively rewrite the origin. + // + // This only works because the remote chain empowers the bridge + // to speak for the local network. + let mut inner_xcm: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + inner_xcm.inner_mut().extend(xcm.into_iter()); + + let (bridge, payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) + .ok_or(err.clone())?; + ensure!(payment.is_none(), err); + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. This will only work if the bridge will do the message + // export for free. Common-good chains will typically be afforded this. + let message = Xcm(vec![ + ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, + }, + ]); + Router::send_xcm(bridge, message) + } +} + +/// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction +/// and sends it to a destination known to be able to handle it. +/// +/// The actual message send to the bridge for forwarding is prepended with `UniversalOrigin` +/// and `DescendOrigin` in order to ensure that the message is executed with this Origin. +/// +/// The `ExportMessage` instruction on the bridge is paid for from the local chain's sovereign +/// account on the bridge. The amount paid is determined through the `ExporterFor` trait. +pub struct SovereignPaidRemoteExporter< + Bridges, + Router, + Ancestry, +>(PhantomData<(Bridges, Router, Ancestry)>); +impl< + Bridges: ExporterFor, + Router: SendXcm, + Ancestry: Get, +> SendXcm for SovereignPaidRemoteExporter { + fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { + let dest = dest.into(); + + // TODO: proper matching so we can be sure that it's the only viable send_xcm before we + // attempt and thus can acceptably consume dest & xcm. + let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); + + let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; + let (remote_network, remote_location, local_network, local_location) = devolved; + + // Prepend the desired message with instructions which effectively rewrite the origin. + // + // This only works because the remote chain empowers the bridge + // to speak for the local network. + let mut inner_xcm: Xcm<()> = vec![ + UniversalOrigin(GlobalConsensus(local_network)), + DescendOrigin(local_location), + ].into(); + inner_xcm.inner_mut().extend(xcm.into_iter()); + + let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) + .ok_or(err.clone())?; + let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge) + .map_err(|_| err.clone())?; + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. This will only work if the bridge will do the message + // export for free. Common-good chains will typically be afforded this. + let export_instruction = ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, + }; + + let message = Xcm(if let Some(payment) = maybe_payment { + vec![ + WithdrawAsset(payment.clone().into()), + BuyExecution { fees: payment, weight_limit: Unlimited }, + export_instruction, + RefundSurplus, + DepositAsset { assets: All.into(), beneficiary: local_from_bridge }, + ] + } else { + vec![export_instruction] + }); + Router::send_xcm(bridge, message) + } +} + +pub trait DispatchBlob { + /// Dispatches an incoming blob and returns the unexpectable weight consumed by the dispatch. + fn dispatch_blob(blob: Vec) -> Result; +} + +pub trait HaulBlob { + /// Sends a blob over some point-to-point link. This will generally be implemented by a bridge. + fn haul_blob(blob: Vec); +} + +#[derive(Clone, Encode, Decode)] +pub struct BridgeMessage { + /// The message destination as a *Universal Location*. This means it begins with a + /// `GlobalConsensus` junction describing the network under which global consensus happens. + /// If this does not match our global consensus then it's a fatal error. + universal_dest: VersionedInteriorMultiLocation, + message: VersionedXcm<()>, +} + +pub enum DispatchBlobError { + Unbridgable, + InvalidEncoding, + UnsupportedLocationVersion, + UnsupportedXcmVersion, + RoutingError, + NonUniversalDestination, + WrongGlobal, +} + +// TODO:: Rename ancestry -> context +// TODO:: Rename InvertLocation -> UniversalLocation + +pub struct BridgeBlobDispatcher(PhantomData<(Router, OurPlace)>); +impl> DispatchBlob for BridgeBlobDispatcher { + fn dispatch_blob(blob: Vec) -> Result { + let our_universal = OurPlace::get(); + let our_global = our_universal.global_consensus() + .map_err(|()| DispatchBlobError::Unbridgable)?; + let BridgeMessage { universal_dest, message } = Decode::decode(&mut &blob[..]) + .map_err(|_| DispatchBlobError::InvalidEncoding)?; + let universal_dest: InteriorMultiLocation = universal_dest.try_into() + .map_err(|_| DispatchBlobError::UnsupportedLocationVersion)?; + // `universal_dest` is the desired destination within the universe: first we need to check + // we're in the right global consensus. + let intended_global = universal_dest.global_consensus() + .map_err(|()| DispatchBlobError::NonUniversalDestination)?; + ensure!(intended_global == our_global, DispatchBlobError::WrongGlobal); + let dest = universal_dest.relative_to(&our_universal); + let message: Xcm<()> = message.try_into() + .map_err(|_| DispatchBlobError::UnsupportedXcmVersion)?; + Router::send_xcm(dest, message).map_err(|_| DispatchBlobError::RoutingError)?; + // TODO: Proper weight. + Ok(0) + } +} + +pub struct HaulBlobExporter(PhantomData<(Bridge, Network)>); +impl> ExportXcm for HaulBlobExporter { + fn export_xcm( + network: NetworkId, + _channel: u32, + destination: impl Into, + message: Xcm<()>, + ) -> Result<(), SendError> { + let destination = destination.into(); + ensure!(network == Network::get(), SendError::CannotReachNetwork(network, destination, message)); + // We don't/can't use the `channel` for this adapter. + let universal_dest = match destination.pushed_front_with(GlobalConsensus(network.clone())) { + Ok(d) => d.into(), + Err((destination, _)) => return Err(SendError::CannotReachNetwork(network, destination, message)), + }; + let message = VersionedXcm::from(message); + Bridge::haul_blob(BridgeMessage { universal_dest, message }.encode()); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cell::RefCell; + use frame_support::parameter_types; + + std::thread_local! { + static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); + } + + struct TestBridge(PhantomData); + impl TestBridge { + fn service() -> u64 { + BRIDGE_TRAFFIC.with(|t| t.borrow_mut().drain(..).map(D::dispatch_blob).sum()) + } + } + impl HaulBlob for TestBridge { + fn haul_blob(blob: Vec) { + BRIDGE_TRAFFIC.with(|t| t.borrow_mut().push(blob)); + } + } + + std::thread_local! { + static REMOTE_INCOMING_XCM: RefCell)>> = RefCell::new(Vec::new()); + } + struct TestRemoteIncomingRouter; + impl SendXcm for TestRemoteIncomingRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push((destination, message))); + } + } + + fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { + REMOTE_INCOMING_XCM.with(|r| core::mem::replace(r.borrow_mut(), vec![])) + } + + parameter_types! { + pub const LocalLocation: Junctions = X1(GlobalConsensus(ByUri(b"local".to_vec()))); + } + parameter_types! { + pub const Remote1: NetworkId = ByUri(b"remote1".to_vec()); + } + + type Router = LocalUnpaidExporter< + HaulBlobExporter< + TestBridge< + BridgeBlobDispatcher + >, + Remote1, + >, + LocalLocation, + >; + + #[test] + fn bridge_works() { + } + + #[test] + fn ensure_is_remote_works() { + // A Kusama parachain is remote from the Polkadot Relay. + let x = ensure_is_remote(Polkadot, (Parent, Kusama, Parachain(1000))); + assert_eq!(x, Ok((Kusama, Parachain(1000).into(), Polkadot, Here))); + + // Polkadot Relay is remote from a Kusama parachain. + let x = ensure_is_remote((Kusama, Parachain(1000)), (Parent, Parent, Polkadot)); + assert_eq!(x, Ok((Polkadot, Here, Kusama, Parachain(1000).into()))); + + // Our own parachain is local. + let x = ensure_is_remote(Polkadot, Parachain(1000)); + assert_eq!(x, Err(Parachain(1000).into())); + + // Polkadot's parachain is not remote if we are Polkadot. + let x = ensure_is_remote(Polkadot, (Parent, Polkadot, Parachain(1000))); + assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); + + // If we don't have a consensus ancestor, then we cannot determine remoteness. + let x = ensure_is_remote((), (Parent, Polkadot, Parachain(1000))); + assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); + } +} \ No newline at end of file diff --git a/xcm/xcm-executor/src/traits/conversion.rs b/xcm/xcm-executor/src/traits/conversion.rs index d82ccfd8e3dd..f7fd19fc824d 100644 --- a/xcm/xcm-executor/src/traits/conversion.rs +++ b/xcm/xcm-executor/src/traits/conversion.rs @@ -209,7 +209,7 @@ pub trait InvertLocation { /// Return the location of the local consensus system from the point of view of the location /// `l`. /// - /// Given a target `location`, the result provides a the location which represents the local + /// Given a target `location`, the result provides the location which represents the local /// consensus system from the targets perspective. fn invert_location(location: &MultiLocation) -> Result { let mut ancestry = Self::universal_location(); From caf33cb7e38943e6137b0c09dd40f190999dbeb4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 31 Jan 2022 17:15:35 +0000 Subject: [PATCH 25/57] Fixes --- xcm/xcm-builder/src/universal_exports.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index c3f15d647a19..c915ff31e15e 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -344,6 +344,7 @@ mod tests { fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { let destination = destination.into(); REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push((destination, message))); + Ok(()) } } From 347b32e0463c3d722f438237523b6455cf9aa77c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 11:51:27 +0000 Subject: [PATCH 26/57] More work --- runtime/test-runtime/src/xcm_config.rs | 4 +-- xcm/pallet-xcm/src/lib.rs | 4 +-- xcm/src/v3/junction.rs | 2 +- xcm/src/v3/multiasset.rs | 38 +++++++++++----------- xcm/src/v3/multilocation.rs | 32 +++++++++--------- xcm/xcm-builder/src/barriers.rs | 2 +- xcm/xcm-builder/src/location_conversion.rs | 14 ++++---- xcm/xcm-builder/src/mock.rs | 2 +- xcm/xcm-builder/src/test_utils.rs | 2 +- xcm/xcm-builder/src/universal_exports.rs | 17 +++++++--- xcm/xcm-executor/src/assets.rs | 8 ++--- xcm/xcm-executor/src/config.rs | 4 +-- xcm/xcm-executor/src/lib.rs | 10 +++--- xcm/xcm-executor/src/traits/conversion.rs | 6 ++-- xcm/xcm-executor/src/traits/mod.rs | 2 +- 15 files changed, 77 insertions(+), 70 deletions(-) diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 82f5b8841820..fe07114c592b 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -22,7 +22,7 @@ use frame_support::{ use xcm::latest::prelude::*; use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, SignedToAccountId32}; use xcm_executor::{ - traits::{InvertLocation, TransactAsset, WeightTrader}, + traits::{UniversalLocation, TransactAsset, WeightTrader}, Assets, }; @@ -72,7 +72,7 @@ impl WeightTrader for DummyWeightTrader { } pub struct InvertNothing; -impl InvertLocation for InvertNothing { +impl UniversalLocation for InvertNothing { fn invert_location(_: &MultiLocation) -> sp_std::result::Result { Ok(Here.into()) } diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index ff4b12d28d08..7c76707e5b8d 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -57,7 +57,7 @@ pub mod pallet { use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, BlockNumberProvider, Hash}; use xcm_executor::{ traits::{ - ClaimAssets, DropAssets, InvertLocation, OnResponse, VersionChangeNotifier, + ClaimAssets, DropAssets, UniversalLocation, OnResponse, VersionChangeNotifier, WeightBounds, }, Assets, @@ -108,7 +108,7 @@ pub mod pallet { type Weigher: WeightBounds<::Call>; /// Means of inverting a location. - type LocationInverter: InvertLocation; + type LocationInverter: UniversalLocation; /// The outer `Origin` type. type Origin: From + From<::Origin>; diff --git a/xcm/src/v3/junction.rs b/xcm/src/v3/junction.rs index 724eb99d7ed9..e29043594e4c 100644 --- a/xcm/src/v3/junction.rs +++ b/xcm/src/v3/junction.rs @@ -125,7 +125,7 @@ pub enum Junction { GeneralKey(Vec), /// The unambiguous child. /// - /// Not currently used except as a fallback when deriving ancestry. + /// Not currently used except as a fallback when deriving context. OnlyChild, /// A pluralistic body existing within consensus. /// diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index 7f9e0ea175b9..a81f75c34078 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -78,10 +78,10 @@ impl AssetId { } /// Mutate the asset to represent the same value from the perspective of a new `target` - /// location. The local chain's location is provided in `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + /// location. The local chain's location is provided in `context`. + pub fn reanchor(&mut self, target: &MultiLocation, context: &MultiLocation) -> Result<(), ()> { if let AssetId::Concrete(ref mut l) = self { - l.reanchor(target, ancestry)?; + l.reanchor(target, context)?; } Ok(()) } @@ -143,19 +143,19 @@ impl MultiAsset { } /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - self.id.reanchor(target, ancestry) + /// relative to a `target` context. The local context is provided as `context`. + pub fn reanchor(&mut self, target: &MultiLocation, context: &MultiLocation) -> Result<(), ()> { + self.id.reanchor(target, context) } /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. + /// relative to a `target` context. The local context is provided as `context`. pub fn reanchored( mut self, target: &MultiLocation, - ancestry: &MultiLocation, + context: &MultiLocation, ) -> Result { - self.id.reanchor(target, ancestry)?; + self.id.reanchor(target, context)?; Ok(self) } @@ -334,9 +334,9 @@ impl MultiAssets { } /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - self.0.iter_mut().try_for_each(|i| i.reanchor(target, ancestry)) + /// relative to a `target` context. The local context is provided as `context`. + pub fn reanchor(&mut self, target: &MultiLocation, context: &MultiLocation) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.reanchor(target, context)) } /// Return a reference to an item at a specific index or `None` if it doesn't exist. @@ -413,12 +413,12 @@ impl WildMultiAsset { } /// Mutate the asset to represent the same value from the perspective of a new `target` - /// location. The local chain's location is provided in `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + /// location. The local chain's location is provided in `context`. + pub fn reanchor(&mut self, target: &MultiLocation, context: &MultiLocation) -> Result<(), ()> { use WildMultiAsset::*; match self { AllOf { ref mut id, .. } | AllOfCounted { ref mut id, .. } => - id.reanchor(target, ancestry), + id.reanchor(target, context), All | AllCounted(_) => Ok(()), } } @@ -498,11 +498,11 @@ impl MultiAssetFilter { } /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + /// relative to a `target` context. The local context is provided as `context`. + pub fn reanchor(&mut self, target: &MultiLocation, context: &MultiLocation) -> Result<(), ()> { match self { - MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, ancestry), - MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(target, ancestry), + MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, context), + MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(target, context), } } diff --git a/xcm/src/v3/multilocation.rs b/xcm/src/v3/multilocation.rs index 33390f68792c..1abf4b80165d 100644 --- a/xcm/src/v3/multilocation.rs +++ b/xcm/src/v3/multilocation.rs @@ -374,20 +374,20 @@ impl MultiLocation { } /// Mutate `self` so that it represents the same location from the point of view of `target`. - /// The context of `self` is provided as `ancestry`. + /// The context of `self` is provided as `context`. /// /// Does not modify `self` in case of overflow. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { + pub fn reanchor(&mut self, target: &MultiLocation, context: &MultiLocation) -> Result<(), ()> { // TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this. - // 1. Use our `ancestry` to figure out how the `target` would address us. - let inverted_target = ancestry.inverted(target)?; + // 1. Use our `context` to figure out how the `target` would address us. + let inverted_target = context.inverted(target)?; // 2. Prepend `inverted_target` to `self` to get self's location from the perspective of // `target`. self.prepend_with(inverted_target).map_err(|_| ())?; - // 3. Given that we know some of `target` ancestry, ensure that any parents in `self` are + // 3. Given that we know some of `target` context, ensure that any parents in `self` are // strictly needed. self.simplify(target.interior()); @@ -395,11 +395,11 @@ impl MultiLocation { } /// Consume `self` and return a new value representing the same location from the point of view - /// of `target`. The context of `self` is provided as `ancestry`. + /// of `target`. The context of `self` is provided as `context`. /// /// Returns the original `self` in case of overflow. - pub fn reanchored(mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result { - match self.reanchor(target, ancestry) { + pub fn reanchored(mut self, target: &MultiLocation, context: &MultiLocation) -> Result { + match self.reanchor(target, context) { Ok(()) => Ok(self), Err(()) => Err(self), } @@ -408,11 +408,11 @@ impl MultiLocation { /// Treating `self` as a context, determine how it would be referenced by a `target` location. pub fn inverted(&self, target: &MultiLocation) -> Result { use Junction::OnlyChild; - let mut ancestry = self.clone(); + let mut context = self.clone(); let mut junctions = Junctions::Here; for _ in 0..target.parent_count() { junctions = junctions - .pushed_front_with(ancestry.interior.take_last().unwrap_or(OnlyChild)) + .pushed_front_with(context.interior.take_last().unwrap_or(OnlyChild)) .map_err(|_| ())?; } let parents = target.interior().len() as u8; @@ -507,16 +507,16 @@ mod tests { #[test] fn inverted_works() { - let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42)).into(); + let context: MultiLocation = (Parachain(1000), PalletInstance(42)).into(); let target = (Parent, PalletInstance(69)).into(); let expected = (Parent, PalletInstance(42)).into(); - let inverted = ancestry.inverted(&target).unwrap(); + let inverted = context.inverted(&target).unwrap(); assert_eq!(inverted, expected); - let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42), GeneralIndex(1)).into(); + let context: MultiLocation = (Parachain(1000), PalletInstance(42), GeneralIndex(1)).into(); let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into(); let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into(); - let inverted = ancestry.inverted(&target).unwrap(); + let inverted = context.inverted(&target).unwrap(); assert_eq!(inverted, expected); } @@ -571,10 +571,10 @@ mod tests { #[test] fn reanchor_works() { let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into(); - let ancestry = Parachain(2000).into(); + let context = Parachain(2000).into(); let target = (Parent, Parachain(1000)).into(); let expected = GeneralIndex(42).into(); - id.reanchor(&target, &ancestry).unwrap(); + id.reanchor(&target, &context).unwrap(); assert_eq!(id, expected); } diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index b0548465c1fd..678458fdc8fe 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -108,7 +108,7 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro /// this. /// /// E.g. -/// ``` +/// ```nocompile /// type MyBarrier = ( /// TakeWeightCredit, /// AllowTopLevelPaidExecutionFrom, diff --git a/xcm/xcm-builder/src/location_conversion.rs b/xcm/xcm-builder/src/location_conversion.rs index 98be5a7ebecb..a40dff5c93af 100644 --- a/xcm/xcm-builder/src/location_conversion.rs +++ b/xcm/xcm-builder/src/location_conversion.rs @@ -20,7 +20,7 @@ use sp_io::hashing::blake2_256; use sp_runtime::traits::{AccountIdConversion, TrailingZeroInput}; use sp_std::{borrow::Borrow, marker::PhantomData}; use xcm::latest::prelude::*; -use xcm_executor::traits::{Convert, InvertLocation}; +use xcm_executor::traits::{Convert, UniversalLocation}; pub struct Account32Hash(PhantomData<(Network, AccountId)>); impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> @@ -147,7 +147,7 @@ impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> } } -/// Simple location inverter; give it this location's ancestry and it'll figure out the inverted +/// Simple location inverter; give it this location's context and it'll figure out the inverted /// location. /// /// # Example @@ -162,7 +162,7 @@ impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> /// # use frame_support::parameter_types; /// # use xcm::latest::prelude::*; /// # use xcm_builder::LocationInverter; -/// # use xcm_executor::traits::InvertLocation; +/// # use xcm_executor::traits::UniversalLocation; /// # fn main() { /// parameter_types!{ /// pub Ancestry: InteriorMultiLocation = X2( @@ -180,7 +180,7 @@ impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> /// # } /// ``` pub struct LocationInverter(PhantomData); -impl> InvertLocation for LocationInverter { +impl> UniversalLocation for LocationInverter { fn universal_location() -> InteriorMultiLocation { Ancestry::get() } @@ -210,7 +210,7 @@ mod tests { // Inputs and outputs written as file paths: // // input location (source to target): ../../../para_2/account32_default - // ancestry (root to source): para_1/account20_default/account20_default + // context (root to source): para_1/account20_default/account20_default // => // output (target to source): ../../para_1/account20_default/account20_default #[test] @@ -229,7 +229,7 @@ mod tests { // Relay -> Para 1 -> SmartContract -> Account // ^ Target #[test] - fn inverter_uses_ancestry_as_inverted_location() { + fn inverter_uses_context_as_inverted_location() { parameter_types! { pub Ancestry: InteriorMultiLocation = X2(account20(), account20()); } @@ -244,7 +244,7 @@ mod tests { // Relay -> Para 1 -> CollectivePallet -> Plurality // ^ Target #[test] - fn inverter_uses_only_child_on_missing_ancestry() { + fn inverter_uses_only_child_on_missing_context() { parameter_types! { pub Ancestry: InteriorMultiLocation = X1(PalletInstance(5)); } diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index ae94a9693114..c49910fa6f81 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -38,7 +38,7 @@ pub use sp_std::{ pub use xcm::latest::prelude::*; pub use xcm_executor::{ traits::{ - ConvertOrigin, ExportXcm, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset, + ConvertOrigin, ExportXcm, FilterAssetLocation, UniversalLocation, OnResponse, TransactAsset, }, Assets, Config, }; diff --git a/xcm/xcm-builder/src/test_utils.rs b/xcm/xcm-builder/src/test_utils.rs index a3742f656da3..edf4bc6285d1 100644 --- a/xcm/xcm-builder/src/test_utils.rs +++ b/xcm/xcm-builder/src/test_utils.rs @@ -25,7 +25,7 @@ use sp_std::vec::Vec; pub use xcm::latest::prelude::*; use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ - traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset}, + traits::{ConvertOrigin, FilterAssetLocation, UniversalLocation, OnResponse, TransactAsset}, Assets, Config, }; diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index c915ff31e15e..9b2559d6972b 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +//! Traits and utilities to help with origin mutation and bridging. + use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; use parity_scale_codec::{Encode, Decode}; use frame_support::{traits::Get, ensure}; @@ -267,7 +269,6 @@ pub enum DispatchBlobError { } // TODO:: Rename ancestry -> context -// TODO:: Rename InvertLocation -> UniversalLocation pub struct BridgeBlobDispatcher(PhantomData<(Router, OurPlace)>); impl> DispatchBlob for BridgeBlobDispatcher { @@ -327,7 +328,7 @@ mod tests { struct TestBridge(PhantomData); impl TestBridge { fn service() -> u64 { - BRIDGE_TRAFFIC.with(|t| t.borrow_mut().drain(..).map(D::dispatch_blob).sum()) + BRIDGE_TRAFFIC.with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).unwrap_or(0)).sum()) } } impl HaulBlob for TestBridge { @@ -349,14 +350,14 @@ mod tests { } fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { - REMOTE_INCOMING_XCM.with(|r| core::mem::replace(r.borrow_mut(), vec![])) + REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) } parameter_types! { - pub const LocalLocation: Junctions = X1(GlobalConsensus(ByUri(b"local".to_vec()))); + pub LocalLocation: Junctions = X1(GlobalConsensus(ByUri(b"local".to_vec()))); } parameter_types! { - pub const Remote1: NetworkId = ByUri(b"remote1".to_vec()); + pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); } type Router = LocalUnpaidExporter< @@ -371,6 +372,12 @@ mod tests { #[test] fn bridge_works() { + let msg = Xcm(vec![Instruction::Trap(1)]); + assert_eq!( + Router::send_xcm(GlobalConsensus(ByUri(b"remote1".to_vec())), msg.clone()), + Ok(()), + ); + assert_eq!(take_received_remote_messages(), vec![(Here.into(), msg)]); } #[test] diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 9c11c9c2485c..7b5d6f4a5855 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -210,20 +210,20 @@ impl Assets { } /// Mutate the assets to be interpreted as the same assets from the perspective of a `target` - /// chain. The local chain's `ancestry` is provided. + /// chain. The local chain's `context` is provided. /// /// Any assets which were unable to be reanchored are introduced into `failed_bin`. pub fn reanchor( &mut self, target: &MultiLocation, - ancestry: &MultiLocation, + context: &MultiLocation, mut maybe_failed_bin: Option<&mut Self>, ) { let mut fungible = Default::default(); mem::swap(&mut self.fungible, &mut fungible); self.fungible = fungible .into_iter() - .filter_map(|(mut id, amount)| match id.reanchor(target, ancestry) { + .filter_map(|(mut id, amount)| match id.reanchor(target, context) { Ok(()) => Some((id, amount)), Err(()) => { maybe_failed_bin.as_mut().map(|f| f.fungible.insert(id, amount)); @@ -235,7 +235,7 @@ impl Assets { mem::swap(&mut self.non_fungible, &mut non_fungible); self.non_fungible = non_fungible .into_iter() - .filter_map(|(mut class, inst)| match class.reanchor(target, ancestry) { + .filter_map(|(mut class, inst)| match class.reanchor(target, context) { Ok(()) => Some((class, inst)), Err(()) => { maybe_failed_bin.as_mut().map(|f| f.non_fungible.insert((class, inst))); diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index bbfb0d808b92..983f7b5cdf68 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use crate::traits::{ - ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, InvertLocation, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, UniversalLocation, OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ @@ -46,7 +46,7 @@ pub trait Config { type IsTeleporter: FilterAssetLocation; /// Means of inverting a location. - type LocationInverter: InvertLocation; + type LocationInverter: UniversalLocation; /// Whether we should execute the given XCM at all. type Barrier: ShouldExecute; diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index f8d4c9095321..2718e0bcee79 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -30,7 +30,7 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, InvertLocation, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, UniversalLocation, OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; @@ -313,8 +313,8 @@ impl XcmExecutor { for asset in assets.inner() { Config::AssetTransactor::beam_asset(asset, origin, &dest)?; } - let ancestry = Config::LocationInverter::universal_location().into(); - assets.reanchor(&dest, &ancestry).map_err(|()| XcmError::MultiLocationFull)?; + let context = Config::LocationInverter::universal_location().into(); + assets.reanchor(&dest, &context).map_err(|()| XcmError::MultiLocationFull)?; let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) @@ -628,8 +628,8 @@ impl XcmExecutor { dest: &MultiLocation, maybe_failed_bin: Option<&mut Assets>, ) -> MultiAssets { - let ancestry = Config::LocationInverter::universal_location().into(); - assets.reanchor(dest, &ancestry, maybe_failed_bin); + let context = Config::LocationInverter::universal_location().into(); + assets.reanchor(dest, &context, maybe_failed_bin); assets.into_assets_iter().collect::>().into() } } diff --git a/xcm/xcm-executor/src/traits/conversion.rs b/xcm/xcm-executor/src/traits/conversion.rs index f7fd19fc824d..b14aedc37b64 100644 --- a/xcm/xcm-executor/src/traits/conversion.rs +++ b/xcm/xcm-executor/src/traits/conversion.rs @@ -205,18 +205,18 @@ impl ConvertOrigin for Tuple { } /// Means of wotking with a relative location. -pub trait InvertLocation { +pub trait UniversalLocation { /// Return the location of the local consensus system from the point of view of the location /// `l`. /// /// Given a target `location`, the result provides the location which represents the local /// consensus system from the targets perspective. fn invert_location(location: &MultiLocation) -> Result { - let mut ancestry = Self::universal_location(); + let mut context = Self::universal_location(); let mut junctions = Here; for _ in 0..location.parent_count() { junctions = junctions - .pushed_with(ancestry.take_first().unwrap_or(OnlyChild)) + .pushed_with(context.take_first().unwrap_or(OnlyChild)) .map_err(|_| ())?; } let parents = location.interior().len() as u8; diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index 8d7ddfef2d6d..efbc69d47563 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -17,7 +17,7 @@ //! Various traits used in configuring the executor. mod conversion; -pub use conversion::{Convert, ConvertOrigin, Decoded, Encoded, Identity, InvertLocation, JustTry}; +pub use conversion::{Convert, ConvertOrigin, Decoded, Encoded, Identity, UniversalLocation, JustTry}; mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; From 8ef2b8435c2e13d3f3ce4ade7faa0940db517122 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 12:46:57 +0000 Subject: [PATCH 27/57] First bridge test passing --- xcm/xcm-builder/src/universal_exports.rs | 52 +++++++++++------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 9b2559d6972b..97505efecefd 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -62,10 +62,11 @@ impl> SendXcm for Loca Err(dest) => return Err(SendError::CannotReachDestination(dest, xcm)), }; let (network, destination, local_network, local_location) = devolved; - let mut message: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); + + let mut message: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); + if local_location != Here { + message.inner_mut().push(DescendOrigin(local_location)); + } message.inner_mut().extend(xcm.into_iter()); Exporter::export_xcm(network, 0, destination, message) } @@ -268,8 +269,6 @@ pub enum DispatchBlobError { WrongGlobal, } -// TODO:: Rename ancestry -> context - pub struct BridgeBlobDispatcher(PhantomData<(Router, OurPlace)>); impl> DispatchBlob for BridgeBlobDispatcher { fn dispatch_blob(blob: Vec) -> Result { @@ -290,12 +289,12 @@ impl> DispatchBlob for Bri .map_err(|_| DispatchBlobError::UnsupportedXcmVersion)?; Router::send_xcm(dest, message).map_err(|_| DispatchBlobError::RoutingError)?; // TODO: Proper weight. - Ok(0) + Ok(1) } } -pub struct HaulBlobExporter(PhantomData<(Bridge, Network)>); -impl> ExportXcm for HaulBlobExporter { +pub struct HaulBlobExporter(PhantomData<(Bridge, BridgedNetwork)>); +impl> ExportXcm for HaulBlobExporter { fn export_xcm( network: NetworkId, _channel: u32, @@ -303,9 +302,10 @@ impl> ExportXcm for HaulBlobExporter, ) -> Result<(), SendError> { let destination = destination.into(); - ensure!(network == Network::get(), SendError::CannotReachNetwork(network, destination, message)); + let bridged_network = BridgedNetwork::get(); + ensure!(&network == &bridged_network, SendError::CannotReachNetwork(network, destination, message)); // We don't/can't use the `channel` for this adapter. - let universal_dest = match destination.pushed_front_with(GlobalConsensus(network.clone())) { + let universal_dest = match destination.pushed_front_with(GlobalConsensus(bridged_network)) { Ok(d) => d.into(), Err((destination, _)) => return Err(SendError::CannotReachNetwork(network, destination, message)), }; @@ -354,30 +354,26 @@ mod tests { } parameter_types! { - pub LocalLocation: Junctions = X1(GlobalConsensus(ByUri(b"local".to_vec()))); - } - parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote1::get())); } - type Router = LocalUnpaidExporter< - HaulBlobExporter< - TestBridge< - BridgeBlobDispatcher - >, - Remote1, - >, - LocalLocation, + type TheBridge = TestBridge< + BridgeBlobDispatcher >; + type Router = LocalUnpaidExporter< HaulBlobExporter, UniversalLocation >; #[test] fn bridge_works() { - let msg = Xcm(vec![Instruction::Trap(1)]); - assert_eq!( - Router::send_xcm(GlobalConsensus(ByUri(b"remote1".to_vec())), msg.clone()), - Ok(()), - ); - assert_eq!(take_received_remote_messages(), vec![(Here.into(), msg)]); + let msg = Xcm(vec![Trap(1)]); + assert_eq!(Router::send_xcm((Parent, ByUri(b"remote1".to_vec())), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!(take_received_remote_messages(), vec![(Here.into(), Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1), + ]))]); } #[test] From b459cfc6a06886687d33487c4552ec8a791ac5a3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 13:04:11 +0000 Subject: [PATCH 28/57] Formatting --- runtime/kusama/src/xcm_config.rs | 8 +- runtime/rococo/src/xcm_config.rs | 2 +- runtime/test-runtime/src/xcm_config.rs | 2 +- runtime/westend/src/xcm_config.rs | 6 +- xcm/pallet-xcm/src/lib.rs | 2 +- xcm/pallet-xcm/src/tests.rs | 6 +- xcm/procedural/src/lib.rs | 2 +- xcm/procedural/src/v3.rs | 13 +- xcm/src/v3/junctions.rs | 26 ++- xcm/src/v3/mod.rs | 26 ++- xcm/src/v3/multilocation.rs | 20 ++- xcm/src/v3/traits.rs | 16 +- xcm/xcm-builder/src/barriers.rs | 34 ++-- xcm/xcm-builder/src/lib.rs | 4 +- xcm/xcm-builder/src/mock.rs | 2 +- xcm/xcm-builder/src/test_utils.rs | 2 +- xcm/xcm-builder/src/tests.rs | 14 +- xcm/xcm-builder/src/universal_exports.rs | 153 +++++++++--------- xcm/xcm-executor/src/config.rs | 5 +- xcm/xcm-executor/src/lib.rs | 18 ++- xcm/xcm-executor/src/traits/mod.rs | 4 +- xcm/xcm-executor/src/traits/should_execute.rs | 2 +- 22 files changed, 204 insertions(+), 163 deletions(-) diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index 170d1fd9ccb3..8cd9983c23b3 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -27,7 +27,6 @@ use frame_support::{ }; use runtime_common::{xcm_sender, ToAuthor}; use xcm::latest::prelude::*; -use xcm_executor::XcmExecutor; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, BackingToPlurality, @@ -36,6 +35,7 @@ use xcm_builder::{ LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; +use xcm_executor::XcmExecutor; parameter_types! { /// The location of the KSM token, from the context of this chain. Since this token is native to this @@ -111,10 +111,8 @@ parameter_types! { pub const KsmForEncointer: (MultiAssetFilter, MultiLocation) = (Ksm::get(), Parachain(1001).into_location()); pub const MaxAssetsIntoHolding: u32 = 64; } -pub type TrustedTeleporters = ( - xcm_builder::Case, - xcm_builder::Case, -); +pub type TrustedTeleporters = + (xcm_builder::Case, xcm_builder::Case); match_type! { pub type OnlyParachains: impl Contains = { diff --git a/runtime/rococo/src/xcm_config.rs b/runtime/rococo/src/xcm_config.rs index 659072b54601..79c27a4ed7b1 100644 --- a/runtime/rococo/src/xcm_config.rs +++ b/runtime/rococo/src/xcm_config.rs @@ -28,13 +28,13 @@ use frame_support::{ use runtime_common::{xcm_sender, ToAuthor}; use sp_std::prelude::*; use xcm::latest::prelude::*; -use xcm_executor::XcmExecutor; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, UsingComponents, }; +use xcm_executor::XcmExecutor; parameter_types! { pub const TokenLocation: MultiLocation = Here.into_location(); diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index fe07114c592b..c1a2eb1e34fa 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -22,7 +22,7 @@ use frame_support::{ use xcm::latest::prelude::*; use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, SignedToAccountId32}; use xcm_executor::{ - traits::{UniversalLocation, TransactAsset, WeightTrader}, + traits::{TransactAsset, UniversalLocation, WeightTrader}, Assets, }; diff --git a/runtime/westend/src/xcm_config.rs b/runtime/westend/src/xcm_config.rs index ef86485dad3e..fc79a86f3398 100644 --- a/runtime/westend/src/xcm_config.rs +++ b/runtime/westend/src/xcm_config.rs @@ -82,10 +82,8 @@ parameter_types! { pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } -pub type TrustedTeleporters = ( - xcm_builder::Case, - xcm_builder::Case, -); +pub type TrustedTeleporters = + (xcm_builder::Case, xcm_builder::Case); /// The barriers one of which must be passed for an XCM message to be executed. pub type Barrier = ( diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 7c76707e5b8d..a1b942906fbd 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -57,7 +57,7 @@ pub mod pallet { use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, BlockNumberProvider, Hash}; use xcm_executor::{ traits::{ - ClaimAssets, DropAssets, UniversalLocation, OnResponse, VersionChangeNotifier, + ClaimAssets, DropAssets, OnResponse, UniversalLocation, VersionChangeNotifier, WeightBounds, }, Assets, diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 391104346345..a67ec11ff75f 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -166,11 +166,7 @@ fn custom_querier_works() { let r = TestNotifier::prepare_new_query(Origin::signed(ALICE), querier.clone()); assert_eq!(r, Ok(())); let status = QueryStatus::Pending { - responder: MultiLocation::from(AccountId32 { - network: None, - id: ALICE.into(), - }) - .into(), + responder: MultiLocation::from(AccountId32 { network: None, id: ALICE.into() }).into(), maybe_notify: None, timeout: 100, maybe_match_querier: Some(querier.clone().into()), diff --git a/xcm/procedural/src/lib.rs b/xcm/procedural/src/lib.rs index d187967485e7..5339247a1597 100644 --- a/xcm/procedural/src/lib.rs +++ b/xcm/procedural/src/lib.rs @@ -20,8 +20,8 @@ use proc_macro::TokenStream; mod v0; mod v1; -mod weight_info; mod v3; +mod weight_info; #[proc_macro] pub fn impl_conversion_functions_for_multilocation_v0(input: TokenStream) -> TokenStream { diff --git a/xcm/procedural/src/v3.rs b/xcm/procedural/src/v3.rs index 52f8733b698d..eeaa76da0985 100644 --- a/xcm/procedural/src/v3.rs +++ b/xcm/procedural/src/v3.rs @@ -67,17 +67,18 @@ pub mod multilocation { }; let from_parent_tuples = (0..=max_parents).map(|cur_parents| { - let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); + let parents = + (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); let underscores = (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); quote! { - impl< #(#types : Into,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation { - fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self { - Self { parents: #cur_parents as u8, interior: #interior } + impl< #(#types : Into,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation { + fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self { + Self { parents: #cur_parents as u8, interior: #interior } + } } - } - } + } }); from_tuple.extend(from_parent_tuples); diff --git a/xcm/src/v3/junctions.rs b/xcm/src/v3/junctions.rs index 1b0f71926c3e..b223dce60eca 100644 --- a/xcm/src/v3/junctions.rs +++ b/xcm/src/v3/junctions.rs @@ -144,7 +144,10 @@ impl Junctions { /// Consumes `self` and returns how `viewer` would address it locally. pub fn relative_to(mut self, viewer: &Junctions) -> MultiLocation { let mut i = 0; - while match (self.first(), viewer.at(i)) { (Some(x), Some(y)) => x == y, _ => false } { + while match (self.first(), viewer.at(i)) { + (Some(x), Some(y)) => x == y, + _ => false, + } { self = self.split_first().0; // NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times. i += 1; @@ -507,9 +510,8 @@ xcm_procedural::impl_conversion_functions_for_junctions_v3!(); #[cfg(test)] mod tests { + use super::{super::prelude::*, *}; use alloc::vec; - use super::*; - use super::super::prelude::*; #[test] fn relative_to_works() { @@ -519,18 +521,9 @@ mod tests { let base = X3(Kusama.into(), Parachain(1), PalletInstance(1)); // Ancestors. - assert_eq!( - Here.relative_to(&base), - (Parent, Parent, Parent).into() - ); - assert_eq!( - X1(Kusama.into()).relative_to(&base), - (Parent, Parent).into() - ); - assert_eq!( - X2(Kusama.into(), Parachain(1)).relative_to(&base), - (Parent,).into() - ); + assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into()); + assert_eq!(X1(Kusama.into()).relative_to(&base), (Parent, Parent).into()); + assert_eq!(X2(Kusama.into(), Parachain(1)).relative_to(&base), (Parent,).into()); assert_eq!( X3(Kusama.into(), Parachain(1), PalletInstance(1)).relative_to(&base), Here.into() @@ -568,7 +561,8 @@ mod tests { (Parent, PalletInstance(2), [1u8; 32]).into() ); assert_eq!( - X5(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into(), vec![1].into()).relative_to(&base), + X5(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into(), vec![1].into()) + .relative_to(&base), ([1u8; 32], vec![1]).into() ); } diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 2210ef39e322..9bfede998da2 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -44,7 +44,7 @@ pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; pub use traits::{ - Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm, Weight, PreparedMessage, + Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, SendResult, SendXcm, Weight, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -79,22 +79,34 @@ impl Xcm { } /// Return a reference to the inner value. - pub fn inner(&self) -> &[Instruction] { &self.0 } + pub fn inner(&self) -> &[Instruction] { + &self.0 + } /// Return a mutable reference to the inner value. - pub fn inner_mut(&mut self) -> &mut Vec> { &mut self.0 } + pub fn inner_mut(&mut self) -> &mut Vec> { + &mut self.0 + } /// Consume and return the inner value. - pub fn into_inner(self) -> Vec> { self.0 } + pub fn into_inner(self) -> Vec> { + self.0 + } /// Return an iterator over references to the items. - pub fn iter(&self) -> impl Iterator> { self.0.iter() } + pub fn iter(&self) -> impl Iterator> { + self.0.iter() + } /// Return an iterator over mutable references to the items. - pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } + pub fn iter_mut(&mut self) -> impl Iterator> { + self.0.iter_mut() + } /// Consume and return an iterator over the items. - pub fn into_iter(self) -> impl Iterator> { self.0.into_iter() } + pub fn into_iter(self) -> impl Iterator> { + self.0.into_iter() + } /// Consume and either return `self` if it contains some instructions, or if it's empty, then /// instead return the result of `f`. diff --git a/xcm/src/v3/multilocation.rs b/xcm/src/v3/multilocation.rs index 1abf4b80165d..e5124be27abd 100644 --- a/xcm/src/v3/multilocation.rs +++ b/xcm/src/v3/multilocation.rs @@ -398,7 +398,11 @@ impl MultiLocation { /// of `target`. The context of `self` is provided as `context`. /// /// Returns the original `self` in case of overflow. - pub fn reanchored(mut self, target: &MultiLocation, context: &MultiLocation) -> Result { + pub fn reanchored( + mut self, + target: &MultiLocation, + context: &MultiLocation, + ) -> Result { match self.reanchor(target, context) { Ok(()) => Ok(self), Err(()) => Err(self), @@ -493,10 +497,10 @@ mod tests { fn conversion_works() { let x: MultiLocation = Parent.into(); assert_eq!(x, MultiLocation { parents: 1, interior: Here }); -// let x: MultiLocation = (Parent,).into(); -// assert_eq!(x, MultiLocation { parents: 1, interior: Here }); -// let x: MultiLocation = (Parent, Parent).into(); -// assert_eq!(x, MultiLocation { parents: 2, interior: Here }); + // let x: MultiLocation = (Parent,).into(); + // assert_eq!(x, MultiLocation { parents: 1, interior: Here }); + // let x: MultiLocation = (Parent, Parent).into(); + // assert_eq!(x, MultiLocation { parents: 2, interior: Here }); let x: MultiLocation = (Parent, Parent, OnlyChild).into(); assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() }); let x: MultiLocation = OnlyChild.into(); @@ -705,11 +709,11 @@ mod tests { takes_multilocation(ParentThen(X1(Parachain(75)))); takes_multilocation([Parachain(100), PalletInstance(3)]); - assert_eq!(v1::MultiLocation::from(v1::Junctions::Here).try_into(), Ok(MultiLocation::here())); assert_eq!( - v1::MultiLocation::from(v1::Parent).try_into(), - Ok(MultiLocation::parent()) + v1::MultiLocation::from(v1::Junctions::Here).try_into(), + Ok(MultiLocation::here()) ); + assert_eq!(v1::MultiLocation::from(v1::Parent).try_into(), Ok(MultiLocation::parent())); assert_eq!( v1::MultiLocation::from(( v1::Parent, diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index f487751be119..3749b74a4e4f 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -274,17 +274,19 @@ pub trait ExecuteXcm { pub enum Weightless {} impl PreparedMessage for Weightless { - fn weight_of(&self) -> Weight { unreachable!() } + fn weight_of(&self) -> Weight { + unreachable!() + } } impl ExecuteXcm for () { type Prepared = Weightless; - fn prepare(message: Xcm) -> result::Result> { Err(message) } - fn execute( - _: impl Into, - _: Self::Prepared, - _: Weight, - ) -> Outcome { unreachable!() } + fn prepare(message: Xcm) -> result::Result> { + Err(message) + } + fn execute(_: impl Into, _: Self::Prepared, _: Weight) -> Outcome { + unreachable!() + } } /// Error result value when attempting to send an XCM message. diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index 678458fdc8fe..a1febc537cd6 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -16,10 +16,18 @@ //! Various implementations for `ShouldExecute`. -use frame_support::{ensure, traits::{Contains, Get}, weights::Weight}; +use frame_support::{ + ensure, + traits::{Contains, Get}, + weights::Weight, +}; use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; -use xcm::latest::{Instruction::{self, *}, Junction, Junctions, MultiLocation, WeightLimit::*}; +use xcm::latest::{ + Instruction::{self, *}, + Junction, Junctions, MultiLocation, + WeightLimit::*, +}; use xcm_executor::traits::{OnResponse, ShouldExecute}; /// Execution barrier that just takes `max_weight` from `weight_credit`. @@ -138,10 +146,9 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro /// would ignore this rule if it began with origin mutators and they changed the origin to something /// which was not on the list. pub struct WithComputedOrigin(PhantomData<(InnerBarrier, MaxPrefixes)>); -impl< - InnerBarrier: ShouldExecute, - MaxPrefixes: Get, -> ShouldExecute for WithComputedOrigin { +impl> ShouldExecute + for WithComputedOrigin +{ fn should_execute( origin: &MultiLocation, instructions: &mut [Instruction], @@ -162,13 +169,22 @@ impl< // invalid UniversalOrigin. while skipped < MaxPrefixes::get() as usize { match instructions.get(skipped) { - Some(UniversalOrigin(j)) => { actual_origin = j.clone().into(); }, - Some(DescendOrigin(j)) => { actual_origin.append_with(j.clone()).map_err(|_| ())?; }, + Some(UniversalOrigin(j)) => { + actual_origin = j.clone().into(); + }, + Some(DescendOrigin(j)) => { + actual_origin.append_with(j.clone()).map_err(|_| ())?; + }, _ => break, } skipped += 1; } - InnerBarrier::should_execute(&actual_origin, &mut instructions[skipped..], max_weight, weight_credit) + InnerBarrier::should_execute( + &actual_origin, + &mut instructions[skipped..], + max_weight, + weight_credit, + ) } } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 9a4945975745..9096d0fca3f6 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -72,6 +72,6 @@ pub use filter_asset_location::{Case, NativeAsset}; mod universal_exports; pub use universal_exports::{ - LocalUnpaidExporter, ExporterFor, NetworkExportTable, UnpaidRemoteExporter, - SovereignPaidRemoteExporter, + ExporterFor, LocalUnpaidExporter, NetworkExportTable, SovereignPaidRemoteExporter, + UnpaidRemoteExporter, }; diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index c49910fa6f81..a1acb655d677 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -38,7 +38,7 @@ pub use sp_std::{ pub use xcm::latest::prelude::*; pub use xcm_executor::{ traits::{ - ConvertOrigin, ExportXcm, FilterAssetLocation, UniversalLocation, OnResponse, TransactAsset, + ConvertOrigin, ExportXcm, FilterAssetLocation, OnResponse, TransactAsset, UniversalLocation, }, Assets, Config, }; diff --git a/xcm/xcm-builder/src/test_utils.rs b/xcm/xcm-builder/src/test_utils.rs index edf4bc6285d1..bdca8f02ff99 100644 --- a/xcm/xcm-builder/src/test_utils.rs +++ b/xcm/xcm-builder/src/test_utils.rs @@ -25,7 +25,7 @@ use sp_std::vec::Vec; pub use xcm::latest::prelude::*; use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ - traits::{ConvertOrigin, FilterAssetLocation, UniversalLocation, OnResponse, TransactAsset}, + traits::{ConvertOrigin, FilterAssetLocation, OnResponse, TransactAsset, UniversalLocation}, Assets, Config, }; diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 2ab8ebe24530..39a92f886a11 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -56,11 +56,21 @@ fn take_weight_credit_barrier_should_work() { let mut message = Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]); let mut weight_credit = 10; - let r = TakeWeightCredit::should_execute(&Parent.into(), message.inner_mut(), 10, &mut weight_credit); + let r = TakeWeightCredit::should_execute( + &Parent.into(), + message.inner_mut(), + 10, + &mut weight_credit, + ); assert_eq!(r, Ok(())); assert_eq!(weight_credit, 0); - let r = TakeWeightCredit::should_execute(&Parent.into(), message.inner_mut(), 10, &mut weight_credit); + let r = TakeWeightCredit::should_execute( + &Parent.into(), + message.inner_mut(), + 10, + &mut weight_credit, + ); assert_eq!(r, Err(())); assert_eq!(weight_credit, 0); } diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 97505efecefd..82337ddba1cf 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -16,9 +16,9 @@ //! Traits and utilities to help with origin mutation and bridging. -use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; -use parity_scale_codec::{Encode, Decode}; -use frame_support::{traits::Get, ensure}; +use frame_support::{ensure, traits::Get}; +use parity_scale_codec::{Decode, Encode}; +use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::ExportXcm; @@ -55,7 +55,9 @@ fn ensure_is_remote( /// /// This is only useful when the local chain has bridging capabilities. pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancestry)>); -impl> SendXcm for LocalUnpaidExporter { +impl> SendXcm + for LocalUnpaidExporter +{ fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { let devolved = match ensure_is_remote(Ancestry::get(), dest) { Ok(x) => x, @@ -103,13 +105,18 @@ impl ExporterFor for Tuple { } pub struct NetworkExportTable(sp_std::marker::PhantomData); -impl)]>> ExporterFor for NetworkExportTable { +impl)]>> ExporterFor + for NetworkExportTable +{ fn exporter_for( network: &NetworkId, _: &InteriorMultiLocation, _: &Xcm<()>, ) -> Option<(MultiLocation, Option)> { - T::get().iter().find(|(ref j, ..)| j == network).map(|(_, l, p)| (l.clone(), p.clone())) + T::get() + .iter() + .find(|(ref j, ..)| j == network) + .map(|(_, l, p)| (l.clone(), p.clone())) } } @@ -125,16 +132,12 @@ impl)]>> Exporter /// /// This is only useful if we have special dispensation by the remote bridges to have the /// `ExportMessage` instruction executed without payment. -pub struct UnpaidRemoteExporter< - Bridges, - Router, - Ancestry, ->(PhantomData<(Bridges, Router, Ancestry)>); -impl< - Bridges: ExporterFor, - Router: SendXcm, - Ancestry: Get, -> SendXcm for UnpaidRemoteExporter { +pub struct UnpaidRemoteExporter( + PhantomData<(Bridges, Router, Ancestry)>, +); +impl> SendXcm + for UnpaidRemoteExporter +{ fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { let dest = dest.into(); @@ -149,26 +152,24 @@ impl< // // This only works because the remote chain empowers the bridge // to speak for the local network. - let mut inner_xcm: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); + let mut inner_xcm: Xcm<()> = + vec![UniversalOrigin(GlobalConsensus(local_network)), DescendOrigin(local_location)] + .into(); inner_xcm.inner_mut().extend(xcm.into_iter()); - let (bridge, payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) - .ok_or(err.clone())?; + let (bridge, payment) = + Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) + .ok_or(err.clone())?; ensure!(payment.is_none(), err); // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message // export for free. Common-good chains will typically be afforded this. - let message = Xcm(vec![ - ExportMessage { - network: remote_network, - destination: remote_location, - xcm: inner_xcm, - }, - ]); + let message = Xcm(vec![ExportMessage { + network: remote_network, + destination: remote_location, + xcm: inner_xcm, + }]); Router::send_xcm(bridge, message) } } @@ -181,16 +182,12 @@ impl< /// /// The `ExportMessage` instruction on the bridge is paid for from the local chain's sovereign /// account on the bridge. The amount paid is determined through the `ExporterFor` trait. -pub struct SovereignPaidRemoteExporter< - Bridges, - Router, - Ancestry, ->(PhantomData<(Bridges, Router, Ancestry)>); -impl< - Bridges: ExporterFor, - Router: SendXcm, - Ancestry: Get, -> SendXcm for SovereignPaidRemoteExporter { +pub struct SovereignPaidRemoteExporter( + PhantomData<(Bridges, Router, Ancestry)>, +); +impl> SendXcm + for SovereignPaidRemoteExporter +{ fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { let dest = dest.into(); @@ -205,25 +202,23 @@ impl< // // This only works because the remote chain empowers the bridge // to speak for the local network. - let mut inner_xcm: Xcm<()> = vec![ - UniversalOrigin(GlobalConsensus(local_network)), - DescendOrigin(local_location), - ].into(); + let mut inner_xcm: Xcm<()> = + vec![UniversalOrigin(GlobalConsensus(local_network)), DescendOrigin(local_location)] + .into(); inner_xcm.inner_mut().extend(xcm.into_iter()); - let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) - .ok_or(err.clone())?; - let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge) + let (bridge, maybe_payment) = + Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) + .ok_or(err.clone())?; + let local_from_bridge = MultiLocation::from(Ancestry::get()) + .inverted(&bridge) .map_err(|_| err.clone())?; // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message // export for free. Common-good chains will typically be afforded this. - let export_instruction = ExportMessage { - network: remote_network, - destination: remote_location, - xcm: inner_xcm, - }; + let export_instruction = + ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }; let message = Xcm(if let Some(payment) = maybe_payment { vec![ @@ -270,23 +265,27 @@ pub enum DispatchBlobError { } pub struct BridgeBlobDispatcher(PhantomData<(Router, OurPlace)>); -impl> DispatchBlob for BridgeBlobDispatcher { +impl> DispatchBlob + for BridgeBlobDispatcher +{ fn dispatch_blob(blob: Vec) -> Result { let our_universal = OurPlace::get(); - let our_global = our_universal.global_consensus() - .map_err(|()| DispatchBlobError::Unbridgable)?; - let BridgeMessage { universal_dest, message } = Decode::decode(&mut &blob[..]) - .map_err(|_| DispatchBlobError::InvalidEncoding)?; - let universal_dest: InteriorMultiLocation = universal_dest.try_into() + let our_global = + our_universal.global_consensus().map_err(|()| DispatchBlobError::Unbridgable)?; + let BridgeMessage { universal_dest, message } = + Decode::decode(&mut &blob[..]).map_err(|_| DispatchBlobError::InvalidEncoding)?; + let universal_dest: InteriorMultiLocation = universal_dest + .try_into() .map_err(|_| DispatchBlobError::UnsupportedLocationVersion)?; // `universal_dest` is the desired destination within the universe: first we need to check // we're in the right global consensus. - let intended_global = universal_dest.global_consensus() + let intended_global = universal_dest + .global_consensus() .map_err(|()| DispatchBlobError::NonUniversalDestination)?; ensure!(intended_global == our_global, DispatchBlobError::WrongGlobal); let dest = universal_dest.relative_to(&our_universal); - let message: Xcm<()> = message.try_into() - .map_err(|_| DispatchBlobError::UnsupportedXcmVersion)?; + let message: Xcm<()> = + message.try_into().map_err(|_| DispatchBlobError::UnsupportedXcmVersion)?; Router::send_xcm(dest, message).map_err(|_| DispatchBlobError::RoutingError)?; // TODO: Proper weight. Ok(1) @@ -294,7 +293,9 @@ impl> DispatchBlob for Bri } pub struct HaulBlobExporter(PhantomData<(Bridge, BridgedNetwork)>); -impl> ExportXcm for HaulBlobExporter { +impl> ExportXcm + for HaulBlobExporter +{ fn export_xcm( network: NetworkId, _channel: u32, @@ -303,11 +304,15 @@ impl> ExportXcm for HaulBlobExp ) -> Result<(), SendError> { let destination = destination.into(); let bridged_network = BridgedNetwork::get(); - ensure!(&network == &bridged_network, SendError::CannotReachNetwork(network, destination, message)); + ensure!( + &network == &bridged_network, + SendError::CannotReachNetwork(network, destination, message) + ); // We don't/can't use the `channel` for this adapter. let universal_dest = match destination.pushed_front_with(GlobalConsensus(bridged_network)) { Ok(d) => d.into(), - Err((destination, _)) => return Err(SendError::CannotReachNetwork(network, destination, message)), + Err((destination, _)) => + return Err(SendError::CannotReachNetwork(network, destination, message)), }; let message = VersionedXcm::from(message); Bridge::haul_blob(BridgeMessage { universal_dest, message }.encode()); @@ -318,8 +323,8 @@ impl> ExportXcm for HaulBlobExp #[cfg(test)] mod tests { use super::*; - use std::cell::RefCell; use frame_support::parameter_types; + use std::cell::RefCell; std::thread_local! { static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); @@ -328,7 +333,8 @@ mod tests { struct TestBridge(PhantomData); impl TestBridge { fn service() -> u64 { - BRIDGE_TRAFFIC.with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).unwrap_or(0)).sum()) + BRIDGE_TRAFFIC + .with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).unwrap_or(0)).sum()) } } impl HaulBlob for TestBridge { @@ -360,20 +366,19 @@ mod tests { pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote1::get())); } - type TheBridge = TestBridge< - BridgeBlobDispatcher - >; - type Router = LocalUnpaidExporter< HaulBlobExporter, UniversalLocation >; + type TheBridge = + TestBridge>; + type Router = LocalUnpaidExporter, UniversalLocation>; #[test] fn bridge_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!(Router::send_xcm((Parent, ByUri(b"remote1".to_vec())), msg), Ok(())); assert_eq!(TheBridge::service(), 1); - assert_eq!(take_received_remote_messages(), vec![(Here.into(), Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1), - ]))]); + assert_eq!( + take_received_remote_messages(), + vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1),]))] + ); } #[test] @@ -398,4 +403,4 @@ mod tests { let x = ensure_is_remote((), (Parent, Polkadot, Parachain(1000))); assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into())); } -} \ No newline at end of file +} diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index 983f7b5cdf68..a008b8e53c7a 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -15,8 +15,9 @@ // along with Polkadot. If not, see . use crate::traits::{ - ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, UniversalLocation, - OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, + ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, + WeightTrader, }; use frame_support::{ dispatch::{Dispatchable, Parameter}, diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 2718e0bcee79..c41b064597e6 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -30,8 +30,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, UniversalLocation, - OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, + ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, + WeightTrader, }; mod assets; @@ -72,9 +73,7 @@ impl PreparedMessage for WeighedMessage { impl ExecuteXcm for XcmExecutor { type Prepared = WeighedMessage; - fn prepare( - mut message: Xcm, - ) -> Result> { + fn prepare(mut message: Xcm) -> Result> { match Config::Weigher::weight(&mut message) { Ok(weight) => Ok(WeighedMessage(weight, message)), Err(_) => Err(message), @@ -93,9 +92,12 @@ impl ExecuteXcm for XcmExecutor { message, weight_credit, ); - if let Err(e) = - Config::Barrier::should_execute(&origin, message.inner_mut(), xcm_weight, &mut weight_credit) - { + if let Err(e) = Config::Barrier::should_execute( + &origin, + message.inner_mut(), + xcm_weight, + &mut weight_credit, + ) { log::debug!( target: "xcm::execute_xcm_in_credit", "Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, weight_credit: {:?})", diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index efbc69d47563..c1e75447d6be 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -17,7 +17,9 @@ //! Various traits used in configuring the executor. mod conversion; -pub use conversion::{Convert, ConvertOrigin, Decoded, Encoded, Identity, UniversalLocation, JustTry}; +pub use conversion::{ + Convert, ConvertOrigin, Decoded, Encoded, Identity, JustTry, UniversalLocation, +}; mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; diff --git a/xcm/xcm-executor/src/traits/should_execute.rs b/xcm/xcm-executor/src/traits/should_execute.rs index d3602c3da2de..2bf781cee559 100644 --- a/xcm/xcm-executor/src/traits/should_execute.rs +++ b/xcm/xcm-executor/src/traits/should_execute.rs @@ -16,7 +16,7 @@ use frame_support::weights::Weight; use sp_std::result::Result; -use xcm::latest::{MultiLocation, Instruction}; +use xcm::latest::{Instruction, MultiLocation}; /// Trait to determine whether the execution engine should actually execute a given XCM. /// From b2d3fdfde572ee1125aec95003f46c467e7e76ce Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 14:22:32 +0000 Subject: [PATCH 29/57] Another test --- xcm/xcm-builder/src/universal_exports.rs | 105 +++++++++++++++++++---- 1 file changed, 88 insertions(+), 17 deletions(-) diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 82337ddba1cf..81082159404a 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -359,26 +359,97 @@ mod tests { REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) } - parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); - pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote1::get())); + /// This test is when we're sending an XCM from a relay-chain which hosts a bridge to another + /// relay-chain. The destination of the XCM is within the global consensus of the + /// remote side of the bridge. + mod local_relay_relay { + use super::*; + + parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote1::get())); + } + type TheBridge = + TestBridge>; + type Router = LocalUnpaidExporter, UniversalLocation>; + + #[test] + fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(Router::send_xcm((Parent, ByUri(b"remote1".to_vec())), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))] + ); + } + + #[test] + fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, ByUri(b"remote1".to_vec()), Parachain(1000)); + assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; + assert_eq!(take_received_remote_messages(), expected); + } } - type TheBridge = - TestBridge>; - type Router = LocalUnpaidExporter, UniversalLocation>; + /// This test is when we're sending an XCM from a parachain which hosts a bridge to another + /// network's bridge parachain. The destination of the XCM is within the global consensus of the + /// remote side of the bridge. + mod local_para_para { + use super::*; + + parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); + pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); + pub RemoteUniversalLocation: Junctions = X2(GlobalConsensus(Remote1::get()), Parachain(1)); + } + type TheBridge = + TestBridge>; + type Router = LocalUnpaidExporter, UniversalLocation>; + + #[test] + fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(Router::send_xcm((Parent, Parent, ByUri(b"remote1".to_vec()), Parachain(1)), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![(Here.into(), Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1).into()), + Trap(1), + ]))] + ); + } - #[test] - fn bridge_works() { - let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, ByUri(b"remote1".to_vec())), msg), Ok(())); - assert_eq!(TheBridge::service(), 1); - assert_eq!( - take_received_remote_messages(), - vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1),]))] - ); + #[test] + fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, Parent, ByUri(b"remote1".to_vec()), Parachain(1000)); + assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + (Parent, Parachain(1000)).into(), + Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } + + #[test] + fn sending_to_relay_chain_of_bridged_chain_works() { + let dest = (Parent, Parent, ByUri(b"remote1".to_vec())); + assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parent.into(), + Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } } #[test] From ca031c2fd6cc515aa6b0ef772617fa8109ff0005 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 17:19:03 +0000 Subject: [PATCH 30/57] Next round of bridging tests --- Cargo.lock | 1 + xcm/xcm-builder/Cargo.toml | 2 + xcm/xcm-builder/src/mock.rs | 21 +- xcm/xcm-builder/src/universal_exports.rs | 447 ++++++++++++++++++++++- 4 files changed, 447 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25d80cfe9063..8c2dbf5f5771 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11906,6 +11906,7 @@ dependencies = [ name = "xcm-builder" version = "0.9.13" dependencies = [ + "assert_matches", "frame-support", "frame-system", "impl-trait-for-tuples", diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index 5165f70e288e..a0326f839d34 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -28,6 +28,8 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-xcm = { path = "../pallet-xcm" } polkadot-runtime-parachains = { path = "../../runtime/parachains" } +assert_matches = "1.5.0" + [features] default = ["std"] runtime-benchmarks = [] diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index a1acb655d677..274f00c09f0b 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -105,6 +105,9 @@ impl GetDispatchInfo for TestCall { thread_local! { pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); pub static EXPORTED_XCM: RefCell> = RefCell::new(Vec::new()); + pub static EXPORTER_OVERRIDE: RefCell) -> SendResult + >> = RefCell::new(None); } pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { SENT_XCM.with(|q| (*q.borrow()).clone()) @@ -112,6 +115,12 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm)> { EXPORTED_XCM.with(|q| (*q.borrow()).clone()) } +pub fn set_exporter_override(f: fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> SendResult) { + EXPORTER_OVERRIDE.with(|x| x.replace(Some(f))); +} +pub fn clear_exporter_override() { + EXPORTER_OVERRIDE.with(|x| x.replace(None)); +} pub struct TestMessageSender; impl SendXcm for TestMessageSender { fn send_xcm(dest: impl Into, msg: opaque::Xcm) -> SendResult { @@ -127,8 +136,12 @@ impl ExportXcm for TestMessageExporter { dest: impl Into, msg: opaque::Xcm, ) -> SendResult { - EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); - Ok(()) + EXPORTER_OVERRIDE.with(|e| if let Some(ref f) = &*e.borrow() { + f(network, channel, dest.into(), msg) + } else { + EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); + Ok(()) + }) } } @@ -285,7 +298,7 @@ pub fn response(query_id: u64) -> Option { } parameter_types! { - pub TestAncestry: InteriorMultiLocation = X1(Parachain(42)); + pub static ExecutorUniversalLocation: InteriorMultiLocation = X1(Parachain(42)); pub UnitWeightCost: Weight = 10; } parameter_types! { @@ -314,7 +327,7 @@ impl Config for TestConfig { type OriginConverter = TestOriginConverter; type IsReserve = TestIsReserve; type IsTeleporter = TestIsTeleporter; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = TestBarrier; type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 81082159404a..5af176328a1e 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -105,7 +105,7 @@ impl ExporterFor for Tuple { } pub struct NetworkExportTable(sp_std::marker::PhantomData); -impl)]>> ExporterFor +impl)>>> ExporterFor for NetworkExportTable { fn exporter_for( @@ -114,9 +114,9 @@ impl)]>> Exporter _: &Xcm<()>, ) -> Option<(MultiLocation, Option)> { T::get() - .iter() + .into_iter() .find(|(ref j, ..)| j == network) - .map(|(_, l, p)| (l.clone(), p.clone())) + .map(|(_, l, p)| (l, p)) } } @@ -152,9 +152,10 @@ impl // // This only works because the remote chain empowers the bridge // to speak for the local network. - let mut inner_xcm: Xcm<()> = - vec![UniversalOrigin(GlobalConsensus(local_network)), DescendOrigin(local_location)] - .into(); + let mut inner_xcm: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); + if local_location != Here { + inner_xcm.inner_mut().push(DescendOrigin(local_location)); + } inner_xcm.inner_mut().extend(xcm.into_iter()); let (bridge, payment) = @@ -202,9 +203,10 @@ impl // // This only works because the remote chain empowers the bridge // to speak for the local network. - let mut inner_xcm: Xcm<()> = - vec![UniversalOrigin(GlobalConsensus(local_network)), DescendOrigin(local_location)] - .into(); + let mut inner_xcm: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); + if local_location != Here { + inner_xcm.inner_mut().push(DescendOrigin(local_location)); + } inner_xcm.inner_mut().extend(xcm.into_iter()); let (bridge, maybe_payment) = @@ -325,6 +327,7 @@ mod tests { use super::*; use frame_support::parameter_types; use std::cell::RefCell; + use assert_matches::assert_matches; std::thread_local! { static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); @@ -359,26 +362,398 @@ mod tests { REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) } + /// This test is when we're sending an XCM from a parachain whose relay-chain hosts a bridge to + /// another relay-chain. The destination of the XCM is within the global consensus of the + /// remote side of the bridge. + + /// local | remote + /// | + /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) + /// /\ | + /// || | + /// || | + /// || | + /// Parachain(1000) | + mod remote_relay_relay { + use xcm_executor::XcmExecutor; + use crate::mock::*; + use super::*; + + parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); + pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), MultiLocation::parent(), None)]; + } + type TheBridge = + TestBridge>; + type RelayExporter = HaulBlobExporter; + type TestExecutor = XcmExecutor; + + /// This is a dummy router which accepts messages destined for our `Parent` and executes + /// them in a context simulated to be like that of our `Parent`. + struct LocalInnerRouter; + impl SendXcm for LocalInnerRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if destination == Parent.into() { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the relay-chain and is getting executed there. We need to + // configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(RelayUniversalLocation::get()); + let origin = UniversalLocation::get().relative_to(&RelayUniversalLocation::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RelayExporter::export_xcm); + // The we execute it: + let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } + } + type LocalBridgingRouter = UnpaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, + >; + type LocalRouter = ( + LocalInnerRouter, + LocalBridgingRouter, + ); + + #[test] + fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm((Parent, Parent, ByUri(b"remote".to_vec())), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )] + ); + } + + /// local | remote + /// | + /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) + /// /\ | || + /// || | || + /// || | || + /// || | \/ + /// Parachain(1000) | Parachain(1000) + #[test] + fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parachain(1000).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } + } + + /// This test is when we're sending an XCM from a parachain whose sibling parachain hosts a + /// bridge to a parachain from another global consensus. The destination of the XCM is within + /// the global consensus of the remote side of the bridge. + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// | + /// | + /// | + /// | + /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) + mod remote_para_para { + use xcm_executor::XcmExecutor; + use crate::mock::*; + use super::*; + + parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); + pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), (Parent, Parachain(1)).into(), None)]; + } + type TheBridge = + TestBridge>; + type RelayExporter = HaulBlobExporter; + type TestExecutor = XcmExecutor; + + /// This is a dummy router which accepts messages destined for our `Parent` and executes + /// them in a context simulated to be like that of our `Parent`. + struct LocalInnerRouter; + impl SendXcm for LocalInnerRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the bridging parachain and is getting executed there. We + // need to configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); + let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RelayExporter::export_xcm); + // The we execute it: + let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } + } + type LocalBridgingRouter = UnpaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, + >; + type LocalRouter = ( + LocalInnerRouter, + LocalBridgingRouter, + ); + + #[test] + fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )] + ); + } + + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// | + /// | + /// | + /// | + /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) ===> Parachain(1000) + #[test] + fn sending_to_sibling_of_bridged_chain_works() { + let dest = (Parent, Parent, Remote::get(), Parachain(1000)); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + (Parent, Parachain(1000)).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } + + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// | /\ + /// | || + /// | || + /// | || + /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) + #[test] + fn sending_to_relay_of_bridged_chain_works() { + let dest = (Parent, Parent, Remote::get()); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parent.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } + } + + /// This test is when we're sending an XCM from a relay-chain whose child parachain hosts a + /// bridge to a parachain from another global consensus. The destination of the XCM is within + /// the global consensus of the remote side of the bridge. + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// || | + /// || | + /// || | + /// \/ | + /// Parachain(1) ===> Parachain(1) + mod remote_para_para_via_relay { + use xcm_executor::XcmExecutor; + use crate::mock::*; + use super::*; + + parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), Parachain(1).into(), None)]; + } + type TheBridge = + TestBridge>; + type RelayExporter = HaulBlobExporter; + type TestExecutor = XcmExecutor; + + /// This is a dummy router which accepts messages destined for our `Parent` and executes + /// them in a context simulated to be like that of our `Parent`. + struct LocalInnerRouter; + impl SendXcm for LocalInnerRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the bridging parachain and is getting executed there. We + // need to configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); + let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RelayExporter::export_xcm); + // The we execute it: + let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } + } + type LocalBridgingRouter = UnpaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, + >; + type LocalRouter = ( + LocalInnerRouter, + LocalBridgingRouter, + ); + + #[test] + fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm((Parent, Remote::get(), Parachain(1)), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1) + ]) + )] + ); + } + + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// || | + /// || | + /// || | + /// \/ | + /// Parachain(1) ===> Parachain(1) ===> Parachain(1000) + #[test] + fn sending_to_sibling_of_bridged_chain_works() { + let dest = (Parent, Remote::get(), Parachain(1000)); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + (Parent, Parachain(1000)).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } + + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// || | /\ + /// || | || + /// || | || + /// \/ | || + /// Parachain(1) ===> Parachain(1) + #[test] + fn sending_to_relay_of_bridged_chain_works() { + let dest = (Parent, Remote::get()); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parent.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); + } + } + /// This test is when we're sending an XCM from a relay-chain which hosts a bridge to another /// relay-chain. The destination of the XCM is within the global consensus of the /// remote side of the bridge. + /// + /// local | remote + /// | + /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) + /// | mod local_relay_relay { use super::*; parameter_types! { pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); - pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote1::get())); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); } type TheBridge = TestBridge>; - type Router = LocalUnpaidExporter, UniversalLocation>; + type Router = LocalUnpaidExporter, UniversalLocation>; #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, ByUri(b"remote1".to_vec())), msg), Ok(())); + assert_eq!(Router::send_xcm((Parent, ByUri(b"remote".to_vec())), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -386,9 +761,17 @@ mod tests { ); } + /// local | remote + /// | + /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) + /// | || + /// | || + /// | || + /// | \/ + /// | Parachain(1000) #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, ByUri(b"remote1".to_vec()), Parachain(1000)); + let dest = (Parent, ByUri(b"remote".to_vec()), Parachain(1000)); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; @@ -399,23 +782,31 @@ mod tests { /// This test is when we're sending an XCM from a parachain which hosts a bridge to another /// network's bridge parachain. The destination of the XCM is within the global consensus of the /// remote side of the bridge. + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// | + /// | + /// | + /// | + /// Parachain(1) ===> Parachain(1) mod local_para_para { use super::*; parameter_types! { pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote1: NetworkId = ByUri(b"remote1".to_vec()); - pub RemoteUniversalLocation: Junctions = X2(GlobalConsensus(Remote1::get()), Parachain(1)); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); } type TheBridge = TestBridge>; - type Router = LocalUnpaidExporter, UniversalLocation>; + type Router = LocalUnpaidExporter, UniversalLocation>; #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, Parent, ByUri(b"remote1".to_vec()), Parachain(1)), msg), Ok(())); + assert_eq!(Router::send_xcm((Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1)), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -427,9 +818,17 @@ mod tests { ); } + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// | + /// | + /// | + /// | + /// Parachain(1) ===> Parachain(1) ==> Parachain(1000) #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote1".to_vec()), Parachain(1000)); + let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( @@ -439,9 +838,17 @@ mod tests { assert_eq!(take_received_remote_messages(), expected); } + /// local | remote + /// | + /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) + /// | /\ + /// | || + /// | || + /// | || + /// Parachain(1) ===> Parachain(1) #[test] fn sending_to_relay_chain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote1".to_vec())); + let dest = (Parent, Parent, ByUri(b"remote".to_vec())); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( From 870b1094e09679863d0d1abd39eb0a57bf4c2e48 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 17:48:15 +0000 Subject: [PATCH 31/57] Repot tests --- .../src/bridging_tests/local_para_para.rs | 94 +++ .../src/bridging_tests/local_relay_relay.rs | 63 +++ xcm/xcm-builder/src/bridging_tests/mod.rs | 62 ++ .../src/bridging_tests/remote_para_para.rs | 145 +++++ .../remote_para_para_via_relay.rs | 142 +++++ .../src/bridging_tests/remote_relay_relay.rs | 119 ++++ xcm/xcm-builder/src/lib.rs | 2 + xcm/xcm-builder/src/universal_exports.rs | 533 ------------------ 8 files changed, 627 insertions(+), 533 deletions(-) create mode 100644 xcm/xcm-builder/src/bridging_tests/local_para_para.rs create mode 100644 xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs create mode 100644 xcm/xcm-builder/src/bridging_tests/mod.rs create mode 100644 xcm/xcm-builder/src/bridging_tests/remote_para_para.rs create mode 100644 xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs create mode 100644 xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs new file mode 100644 index 000000000000..a303a626e8a6 --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -0,0 +1,94 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! This test is when we're sending an XCM from a parachain which hosts a bridge to another +//! network's bridge parachain. The destination of the XCM is within the global consensus of the +//! remote side of the bridge. + +use super::*; + +parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); +} +type TheBridge = + TestBridge>; +type Router = LocalUnpaidExporter, UniversalLocation>; + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// | +/// | +/// | +/// | +/// Parachain(1) ===> Parachain(1) +#[test] +fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(Router::send_xcm((Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1)), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![(Here.into(), Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1).into()), + Trap(1), + ]))] + ); +} + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// | +/// | +/// | +/// | +/// Parachain(1) ===> Parachain(1) ==> Parachain(1000) +#[test] +fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + (Parent, Parachain(1000)).into(), + Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// | /\ +/// | || +/// | || +/// | || +/// Parachain(1) ===> Parachain(1) +#[test] +fn sending_to_relay_chain_of_bridged_chain_works() { + let dest = (Parent, Parent, ByUri(b"remote".to_vec())); + assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parent.into(), + Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs new file mode 100644 index 000000000000..88556a06bff3 --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -0,0 +1,63 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! This test is when we're sending an XCM from a relay-chain which hosts a bridge to another +//! relay-chain. The destination of the XCM is within the global consensus of the +//! remote side of the bridge. + +use super::*; + +parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); +} +type TheBridge = + TestBridge>; +type Router = LocalUnpaidExporter, UniversalLocation>; + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) +/// | +#[test] +fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(Router::send_xcm((Parent, ByUri(b"remote".to_vec())), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))] + ); +} + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) +/// | || +/// | || +/// | || +/// | \/ +/// | Parachain(1000) +#[test] +fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; + assert_eq!(take_received_remote_messages(), expected); +} diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs new file mode 100644 index 000000000000..3f113938fca0 --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -0,0 +1,62 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Tests specific to the bridging primitives + +use std::{cell::RefCell, marker::PhantomData}; +use frame_support::parameter_types; +use assert_matches::assert_matches; +use xcm::prelude::*; +use crate::universal_exports::*; + +mod local_relay_relay; +mod local_para_para; +mod remote_relay_relay; +mod remote_para_para; +mod remote_para_para_via_relay; + +std::thread_local! { + static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); +} + +struct TestBridge(PhantomData); +impl TestBridge { + fn service() -> u64 { + BRIDGE_TRAFFIC + .with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).unwrap_or(0)).sum()) + } +} +impl HaulBlob for TestBridge { + fn haul_blob(blob: Vec) { + BRIDGE_TRAFFIC.with(|t| t.borrow_mut().push(blob)); + } +} + +std::thread_local! { + static REMOTE_INCOMING_XCM: RefCell)>> = RefCell::new(Vec::new()); +} +struct TestRemoteIncomingRouter; +impl SendXcm for TestRemoteIncomingRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push((destination, message))); + Ok(()) + } +} + +fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { + REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) +} diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs new file mode 100644 index 000000000000..c2bc0df60bd1 --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -0,0 +1,145 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! This test is when we're sending an XCM from a parachain whose sibling parachain hosts a +//! bridge to a parachain from another global consensus. The destination of the XCM is within +//! the global consensus of the remote side of the bridge. + +use xcm_executor::XcmExecutor; +use crate::mock::*; +use super::*; + +parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); + pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), (Parent, Parachain(1)).into(), None)]; +} +type TheBridge = + TestBridge>; +type RelayExporter = HaulBlobExporter; +type TestExecutor = XcmExecutor; + +/// This is a dummy router which accepts messages destined for our `Parent` and executes +/// them in a context simulated to be like that of our `Parent`. +struct LocalInnerRouter; +impl SendXcm for LocalInnerRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the bridging parachain and is getting executed there. We + // need to configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); + let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RelayExporter::export_xcm); + // The we execute it: + let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } +} +type LocalBridgingRouter = UnpaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, +>; +type LocalRouter = ( + LocalInnerRouter, + LocalBridgingRouter, +); + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// | +/// | +/// | +/// | +/// Parachain(1000) ===> Parachain(1) ===> Parachain(1) +#[test] +fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )] + ); +} + +/// +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// | +/// | +/// | +/// | +/// Parachain(1000) ===> Parachain(1) ===> Parachain(1) ===> Parachain(1000) +#[test] +fn sending_to_sibling_of_bridged_chain_works() { + let dest = (Parent, Parent, Remote::get(), Parachain(1000)); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + (Parent, Parachain(1000)).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} + +/// +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// | /\ +/// | || +/// | || +/// | || +/// Parachain(1000) ===> Parachain(1) ===> Parachain(1) +#[test] +fn sending_to_relay_of_bridged_chain_works() { + let dest = (Parent, Parent, Remote::get()); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parent.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs new file mode 100644 index 000000000000..1d27c604adc0 --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -0,0 +1,142 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! This test is when we're sending an XCM from a relay-chain whose child parachain hosts a +//! bridge to a parachain from another global consensus. The destination of the XCM is within +//! the global consensus of the remote side of the bridge. + +use xcm_executor::XcmExecutor; +use crate::mock::*; +use super::*; + +parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), Parachain(1).into(), None)]; +} +type TheBridge = + TestBridge>; +type RelayExporter = HaulBlobExporter; +type TestExecutor = XcmExecutor; + +/// This is a dummy router which accepts messages destined for our `Parent` and executes +/// them in a context simulated to be like that of our `Parent`. +struct LocalInnerRouter; +impl SendXcm for LocalInnerRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the bridging parachain and is getting executed there. We + // need to configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); + let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RelayExporter::export_xcm); + // The we execute it: + let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } +} +type LocalBridgingRouter = UnpaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, +>; +type LocalRouter = ( + LocalInnerRouter, + LocalBridgingRouter, +); + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// || | +/// || | +/// || | +/// \/ | +/// Parachain(1) ===> Parachain(1) +#[test] +fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm((Parent, Remote::get(), Parachain(1)), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1) + ]) + )] + ); +} + +/// +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// || | +/// || | +/// || | +/// \/ | +/// Parachain(1) ===> Parachain(1) ===> Parachain(1000) +#[test] +fn sending_to_sibling_of_bridged_chain_works() { + let dest = (Parent, Remote::get(), Parachain(1000)); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + (Parent, Parachain(1000)).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} + +/// +/// local | remote +/// | +/// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) +/// || | /\ +/// || | || +/// || | || +/// \/ | || +/// Parachain(1) ===> Parachain(1) +#[test] +fn sending_to_relay_of_bridged_chain_works() { + let dest = (Parent, Remote::get()); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parent.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs new file mode 100644 index 000000000000..d94ab1e4ca7d --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -0,0 +1,119 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! This test is when we're sending an XCM from a parachain whose relay-chain hosts a bridge to +//! another relay-chain. The destination of the XCM is within the global consensus of the +//! remote side of the bridge. + +use xcm_executor::XcmExecutor; +use crate::mock::*; +use super::*; + +parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); + pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), MultiLocation::parent(), None)]; +} +type TheBridge = + TestBridge>; +type RelayExporter = HaulBlobExporter; +type TestExecutor = XcmExecutor; + +/// This is a dummy router which accepts messages destined for our `Parent` and executes +/// them in a context simulated to be like that of our `Parent`. +struct LocalInnerRouter; +impl SendXcm for LocalInnerRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if destination == Parent.into() { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the relay-chain and is getting executed there. We need to + // configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(RelayUniversalLocation::get()); + let origin = UniversalLocation::get().relative_to(&RelayUniversalLocation::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RelayExporter::export_xcm); + // The we execute it: + let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } +} +type LocalBridgingRouter = UnpaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, +>; +type LocalRouter = ( + LocalInnerRouter, + LocalBridgingRouter, +); + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) +/// /\ | +/// || | +/// || | +/// || | +/// Parachain(1000) | +#[test] +fn sending_to_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm((Parent, Parent, ByUri(b"remote".to_vec())), msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )] + ); +} + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) +/// /\ | || +/// || | || +/// || | || +/// || | \/ +/// Parachain(1000) | Parachain(1000) +#[test] +fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parachain(1000).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1000).into()), + Trap(1) + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); +} diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 9096d0fca3f6..4bf9ed6b3cba 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -24,6 +24,8 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(test)] +mod bridging_tests; #[cfg(feature = "std")] pub mod test_utils; diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 5af176328a1e..161f6a6b8f9f 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -325,539 +325,6 @@ impl> ExportXcm #[cfg(test)] mod tests { use super::*; - use frame_support::parameter_types; - use std::cell::RefCell; - use assert_matches::assert_matches; - - std::thread_local! { - static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); - } - - struct TestBridge(PhantomData); - impl TestBridge { - fn service() -> u64 { - BRIDGE_TRAFFIC - .with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).unwrap_or(0)).sum()) - } - } - impl HaulBlob for TestBridge { - fn haul_blob(blob: Vec) { - BRIDGE_TRAFFIC.with(|t| t.borrow_mut().push(blob)); - } - } - - std::thread_local! { - static REMOTE_INCOMING_XCM: RefCell)>> = RefCell::new(Vec::new()); - } - struct TestRemoteIncomingRouter; - impl SendXcm for TestRemoteIncomingRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push((destination, message))); - Ok(()) - } - } - - fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { - REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) - } - - /// This test is when we're sending an XCM from a parachain whose relay-chain hosts a bridge to - /// another relay-chain. The destination of the XCM is within the global consensus of the - /// remote side of the bridge. - - /// local | remote - /// | - /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) - /// /\ | - /// || | - /// || | - /// || | - /// Parachain(1000) | - mod remote_relay_relay { - use xcm_executor::XcmExecutor; - use crate::mock::*; - use super::*; - - parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); - pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); - pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); - pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> - = vec![(Remote::get(), MultiLocation::parent(), None)]; - } - type TheBridge = - TestBridge>; - type RelayExporter = HaulBlobExporter; - type TestExecutor = XcmExecutor; - - /// This is a dummy router which accepts messages destined for our `Parent` and executes - /// them in a context simulated to be like that of our `Parent`. - struct LocalInnerRouter; - impl SendXcm for LocalInnerRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if destination == Parent.into() { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the relay-chain and is getting executed there. We need to - // configure the TestExecutor appropriately: - ExecutorUniversalLocation::set(RelayUniversalLocation::get()); - let origin = UniversalLocation::get().relative_to(&RelayUniversalLocation::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RelayExporter::export_xcm); - // The we execute it: - let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) - } - Err(SendError::CannotReachDestination(destination, message)) - } - } - type LocalBridgingRouter = UnpaidRemoteExporter< - NetworkExportTable, - LocalInnerRouter, - UniversalLocation, - >; - type LocalRouter = ( - LocalInnerRouter, - LocalBridgingRouter, - ); - - #[test] - fn sending_to_bridged_chain_works() { - let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Parent, ByUri(b"remote".to_vec())), msg), Ok(())); - assert_eq!(TheBridge::service(), 1); - assert_eq!( - take_received_remote_messages(), - vec![( - Here.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) - )] - ); - } - - /// local | remote - /// | - /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) - /// /\ | || - /// || | || - /// || | || - /// || | \/ - /// Parachain(1000) | Parachain(1000) - #[test] - fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - Parachain(1000).into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - } - - /// This test is when we're sending an XCM from a parachain whose sibling parachain hosts a - /// bridge to a parachain from another global consensus. The destination of the XCM is within - /// the global consensus of the remote side of the bridge. - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// | - /// | - /// | - /// | - /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) - mod remote_para_para { - use xcm_executor::XcmExecutor; - use crate::mock::*; - use super::*; - - parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); - pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); - pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); - pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> - = vec![(Remote::get(), (Parent, Parachain(1)).into(), None)]; - } - type TheBridge = - TestBridge>; - type RelayExporter = HaulBlobExporter; - type TestExecutor = XcmExecutor; - - /// This is a dummy router which accepts messages destined for our `Parent` and executes - /// them in a context simulated to be like that of our `Parent`. - struct LocalInnerRouter; - impl SendXcm for LocalInnerRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the bridging parachain and is getting executed there. We - // need to configure the TestExecutor appropriately: - ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); - let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RelayExporter::export_xcm); - // The we execute it: - let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) - } - Err(SendError::CannotReachDestination(destination, message)) - } - } - type LocalBridgingRouter = UnpaidRemoteExporter< - NetworkExportTable, - LocalInnerRouter, - UniversalLocation, - >; - type LocalRouter = ( - LocalInnerRouter, - LocalBridgingRouter, - ); - - #[test] - fn sending_to_bridged_chain_works() { - let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), Ok(())); - assert_eq!(TheBridge::service(), 1); - assert_eq!( - take_received_remote_messages(), - vec![( - Here.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) - )] - ); - } - - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// | - /// | - /// | - /// | - /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) ===> Parachain(1000) - #[test] - fn sending_to_sibling_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get(), Parachain(1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - (Parent, Parachain(1000)).into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// | /\ - /// | || - /// | || - /// | || - /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) - #[test] - fn sending_to_relay_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get()); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - Parent.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - } - - /// This test is when we're sending an XCM from a relay-chain whose child parachain hosts a - /// bridge to a parachain from another global consensus. The destination of the XCM is within - /// the global consensus of the remote side of the bridge. - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// || | - /// || | - /// || | - /// \/ | - /// Parachain(1) ===> Parachain(1) - mod remote_para_para_via_relay { - use xcm_executor::XcmExecutor; - use crate::mock::*; - use super::*; - - parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); - pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); - pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> - = vec![(Remote::get(), Parachain(1).into(), None)]; - } - type TheBridge = - TestBridge>; - type RelayExporter = HaulBlobExporter; - type TestExecutor = XcmExecutor; - - /// This is a dummy router which accepts messages destined for our `Parent` and executes - /// them in a context simulated to be like that of our `Parent`. - struct LocalInnerRouter; - impl SendXcm for LocalInnerRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the bridging parachain and is getting executed there. We - // need to configure the TestExecutor appropriately: - ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); - let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RelayExporter::export_xcm); - // The we execute it: - let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) - } - Err(SendError::CannotReachDestination(destination, message)) - } - } - type LocalBridgingRouter = UnpaidRemoteExporter< - NetworkExportTable, - LocalInnerRouter, - UniversalLocation, - >; - type LocalRouter = ( - LocalInnerRouter, - LocalBridgingRouter, - ); - - #[test] - fn sending_to_bridged_chain_works() { - let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Remote::get(), Parachain(1)), msg), Ok(())); - assert_eq!(TheBridge::service(), 1); - assert_eq!( - take_received_remote_messages(), - vec![( - Here.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1) - ]) - )] - ); - } - - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// || | - /// || | - /// || | - /// \/ | - /// Parachain(1) ===> Parachain(1) ===> Parachain(1000) - #[test] - fn sending_to_sibling_of_bridged_chain_works() { - let dest = (Parent, Remote::get(), Parachain(1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - (Parent, Parachain(1000)).into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1) - ]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// || | /\ - /// || | || - /// || | || - /// \/ | || - /// Parachain(1) ===> Parachain(1) - #[test] - fn sending_to_relay_of_bridged_chain_works() { - let dest = (Parent, Remote::get()); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - Parent.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1) - ]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - } - - /// This test is when we're sending an XCM from a relay-chain which hosts a bridge to another - /// relay-chain. The destination of the XCM is within the global consensus of the - /// remote side of the bridge. - /// - /// local | remote - /// | - /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) - /// | - mod local_relay_relay { - use super::*; - - parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); - pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); - } - type TheBridge = - TestBridge>; - type Router = LocalUnpaidExporter, UniversalLocation>; - - #[test] - fn sending_to_bridged_chain_works() { - let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, ByUri(b"remote".to_vec())), msg), Ok(())); - assert_eq!(TheBridge::service(), 1); - assert_eq!( - take_received_remote_messages(), - vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))] - ); - } - - /// local | remote - /// | - /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) - /// | || - /// | || - /// | || - /// | \/ - /// | Parachain(1000) - #[test] - fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, ByUri(b"remote".to_vec()), Parachain(1000)); - assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; - assert_eq!(take_received_remote_messages(), expected); - } - } - - /// This test is when we're sending an XCM from a parachain which hosts a bridge to another - /// network's bridge parachain. The destination of the XCM is within the global consensus of the - /// remote side of the bridge. - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// | - /// | - /// | - /// | - /// Parachain(1) ===> Parachain(1) - mod local_para_para { - use super::*; - - parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); - pub RemoteUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); - } - type TheBridge = - TestBridge>; - type Router = LocalUnpaidExporter, UniversalLocation>; - - #[test] - fn sending_to_bridged_chain_works() { - let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1)), msg), Ok(())); - assert_eq!(TheBridge::service(), 1); - assert_eq!( - take_received_remote_messages(), - vec![(Here.into(), Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1).into()), - Trap(1), - ]))] - ); - } - - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// | - /// | - /// | - /// | - /// Parachain(1) ===> Parachain(1) ==> Parachain(1000) - #[test] - fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); - assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - (Parent, Parachain(1000)).into(), - Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - - /// local | remote - /// | - /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) - /// | /\ - /// | || - /// | || - /// | || - /// Parachain(1) ===> Parachain(1) - #[test] - fn sending_to_relay_chain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote".to_vec())); - assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); - assert_eq!(TheBridge::service(), 1); - let expected = vec![( - Parent.into(), - Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) - )]; - assert_eq!(take_received_remote_messages(), expected); - } - } #[test] fn ensure_is_remote_works() { From c68bbc89387a69d3bdbd94c9eae623f8b9b80bcb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 18:06:59 +0000 Subject: [PATCH 32/57] Cleanups --- .../src/bridging_tests/local_para_para.rs | 8 ++--- .../src/bridging_tests/local_relay_relay.rs | 6 ++-- xcm/xcm-builder/src/bridging_tests/mod.rs | 32 ++++++++++++++++- .../src/bridging_tests/remote_para_para.rs | 28 +-------------- .../remote_para_para_via_relay.rs | 28 +-------------- .../src/bridging_tests/remote_relay_relay.rs | 36 +++---------------- 6 files changed, 43 insertions(+), 95 deletions(-) diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index a303a626e8a6..870eacb83ce4 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -21,9 +21,7 @@ use super::*; parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); pub RemoteUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); } type TheBridge = @@ -41,7 +39,7 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1)), msg), Ok(())); + assert_eq!(Router::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -63,7 +61,7 @@ fn sending_to_bridged_chain_works() { /// Parachain(1) ===> Parachain(1) ==> Parachain(1000) #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + let dest = (Parent, Parent, Remote::get(), Parachain(1000)); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( @@ -83,7 +81,7 @@ fn sending_to_parachain_of_bridged_chain_works() { /// Parachain(1) ===> Parachain(1) #[test] fn sending_to_relay_chain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote".to_vec())); + let dest = (Parent, Parent, Remote::get()); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs index 88556a06bff3..8a147988ca8e 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -21,9 +21,7 @@ use super::*; parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); } type TheBridge = @@ -37,7 +35,7 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, ByUri(b"remote".to_vec())), msg), Ok(())); + assert_eq!(Router::send_xcm((Parent, Remote::get()), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -55,7 +53,7 @@ fn sending_to_bridged_chain_works() { /// | Parachain(1000) #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + let dest = (Parent, Remote::get(), Parachain(1000)); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index 3f113938fca0..47e10c084385 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -17,10 +17,12 @@ //! Tests specific to the bridging primitives use std::{cell::RefCell, marker::PhantomData}; -use frame_support::parameter_types; +use frame_support::{parameter_types, traits::Get}; use assert_matches::assert_matches; use xcm::prelude::*; +use xcm_executor::XcmExecutor; use crate::universal_exports::*; +use crate::mock::*; mod local_relay_relay; mod local_para_para; @@ -28,6 +30,11 @@ mod remote_relay_relay; mod remote_para_para; mod remote_para_para_via_relay; +parameter_types! { + pub Local: NetworkId = ByUri(b"local".to_vec()); + pub Remote: NetworkId = ByUri(b"remote".to_vec()); +} + std::thread_local! { static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); } @@ -60,3 +67,26 @@ impl SendXcm for TestRemoteIncomingRouter { fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) } + +/// This is a dummy router which accepts messages destined for `Remote` from `Local` +/// them in a context simulated to be like that of our `Parent`. +struct ExecutingRouter(PhantomData<(Local, Remote, RemoteExporter)>); +impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if destination == Remote::get().relative_to(&Local::get()) { + // We now pretend that the message was delivered from the local chain + // Parachain(1000) to the relay-chain and is getting executed there. We need to + // configure the TestExecutor appropriately: + ExecutorUniversalLocation::set(Remote::get()); + let origin = Local::get().relative_to(&Remote::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RemoteExporter::export_xcm); + // The we execute it: + let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + assert_matches!(outcome, Outcome::Complete(_)); + return Ok(()) + } + Err(SendError::CannotReachDestination(destination, message)) + } +} diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index c2bc0df60bd1..90d482a0b8df 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -18,15 +18,12 @@ //! bridge to a parachain from another global consensus. The destination of the XCM is within //! the global consensus of the remote side of the bridge. -use xcm_executor::XcmExecutor; use crate::mock::*; use super::*; parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> = vec![(Remote::get(), (Parent, Parachain(1)).into(), None)]; @@ -34,30 +31,7 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type TestExecutor = XcmExecutor; - -/// This is a dummy router which accepts messages destined for our `Parent` and executes -/// them in a context simulated to be like that of our `Parent`. -struct LocalInnerRouter; -impl SendXcm for LocalInnerRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the bridging parachain and is getting executed there. We - // need to configure the TestExecutor appropriately: - ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); - let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RelayExporter::export_xcm); - // The we execute it: - let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) - } - Err(SendError::CannotReachDestination(destination, message)) - } -} +type LocalInnerRouter = ExecutingRouter; type LocalBridgingRouter = UnpaidRemoteExporter< NetworkExportTable, LocalInnerRouter, diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index 1d27c604adc0..fe6c1a02f314 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -18,15 +18,12 @@ //! bridge to a parachain from another global consensus. The destination of the XCM is within //! the global consensus of the remote side of the bridge. -use xcm_executor::XcmExecutor; use crate::mock::*; use super::*; parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1)); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1)); pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> = vec![(Remote::get(), Parachain(1).into(), None)]; @@ -34,30 +31,7 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type TestExecutor = XcmExecutor; - -/// This is a dummy router which accepts messages destined for our `Parent` and executes -/// them in a context simulated to be like that of our `Parent`. -struct LocalInnerRouter; -impl SendXcm for LocalInnerRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if ParaBridgeUniversalLocation::get().relative_to(&UniversalLocation::get()) == destination { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the bridging parachain and is getting executed there. We - // need to configure the TestExecutor appropriately: - ExecutorUniversalLocation::set(ParaBridgeUniversalLocation::get()); - let origin = UniversalLocation::get().relative_to(&ParaBridgeUniversalLocation::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RelayExporter::export_xcm); - // The we execute it: - let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) - } - Err(SendError::CannotReachDestination(destination, message)) - } -} +type LocalInnerRouter = ExecutingRouter; type LocalBridgingRouter = UnpaidRemoteExporter< NetworkExportTable, LocalInnerRouter, diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index d94ab1e4ca7d..8462d89140a1 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -18,15 +18,12 @@ //! another relay-chain. The destination of the XCM is within the global consensus of the //! remote side of the bridge. -use xcm_executor::XcmExecutor; use crate::mock::*; use super::*; parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> = vec![(Remote::get(), MultiLocation::parent(), None)]; @@ -34,38 +31,15 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type TestExecutor = XcmExecutor; - -/// This is a dummy router which accepts messages destined for our `Parent` and executes -/// them in a context simulated to be like that of our `Parent`. -struct LocalInnerRouter; -impl SendXcm for LocalInnerRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if destination == Parent.into() { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the relay-chain and is getting executed there. We need to - // configure the TestExecutor appropriately: - ExecutorUniversalLocation::set(RelayUniversalLocation::get()); - let origin = UniversalLocation::get().relative_to(&RelayUniversalLocation::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RelayExporter::export_xcm); - // The we execute it: - let outcome = TestExecutor::execute_xcm(origin, message.into(), 1_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) - } - Err(SendError::CannotReachDestination(destination, message)) - } -} -type LocalBridgingRouter = UnpaidRemoteExporter< +type LocalInnerRouter = ExecutingRouter; +type LocalBridgeRouter = UnpaidRemoteExporter< NetworkExportTable, LocalInnerRouter, UniversalLocation, >; type LocalRouter = ( LocalInnerRouter, - LocalBridgingRouter, + LocalBridgeRouter, ); /// local | remote @@ -79,7 +53,7 @@ type LocalRouter = ( #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Parent, ByUri(b"remote".to_vec())), msg), Ok(())); + assert_eq!(::send_xcm((Parent, Parent, Remote::get()), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -104,7 +78,7 @@ fn sending_to_bridged_chain_works() { /// Parachain(1000) | Parachain(1000) #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, ByUri(b"remote".to_vec()), Parachain(1000)); + let dest = (Parent, Parent, Remote::get(), Parachain(1000)); assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( From 1724115d2c0e198c42d76f178630e514cf159c9b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 19:30:16 +0000 Subject: [PATCH 33/57] Paid bridging --- xcm/xcm-builder/src/bridging_tests/mod.rs | 46 +++++-- .../bridging_tests/paid_remote_relay_relay.rs | 121 ++++++++++++++++++ .../src/bridging_tests/remote_para_para.rs | 2 +- .../remote_para_para_via_relay.rs | 2 +- .../src/bridging_tests/remote_relay_relay.rs | 2 +- xcm/xcm-builder/src/mock.rs | 6 +- xcm/xcm-builder/src/tests.rs | 10 +- 7 files changed, 170 insertions(+), 19 deletions(-) create mode 100644 xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index 47e10c084385..eecbd54aa589 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -18,7 +18,6 @@ use std::{cell::RefCell, marker::PhantomData}; use frame_support::{parameter_types, traits::Get}; -use assert_matches::assert_matches; use xcm::prelude::*; use xcm_executor::XcmExecutor; use crate::universal_exports::*; @@ -29,6 +28,7 @@ mod local_para_para; mod remote_relay_relay; mod remote_para_para; mod remote_para_para_via_relay; +mod paid_remote_relay_relay; parameter_types! { pub Local: NetworkId = ByUri(b"local".to_vec()); @@ -69,23 +69,53 @@ fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { } /// This is a dummy router which accepts messages destined for `Remote` from `Local` -/// them in a context simulated to be like that of our `Parent`. +/// and then executes them for free in a context simulated to be like that of our `Remote`. +struct UnpaidExecutingRouter(PhantomData<(Local, Remote, RemoteExporter)>); +impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { + fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + let destination = destination.into(); + if destination == Remote::get().relative_to(&Local::get()) { + // We now pretend that the message was delivered from `Local` to `Remote`, and execute + // so we need to ensure that the `TestConfig` is set up properly for executing as + // though it is `Remote`. + ExecutorUniversalLocation::set(Remote::get()); + let origin = Local::get().relative_to(&Remote::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RemoteExporter::export_xcm); + // The we execute it: + let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + return match outcome { + Outcome::Complete(..) => Ok(()), + Outcome::Incomplete(..) => Err(SendError::Transport("Error executing")), + Outcome::Error(..) => Err(SendError::Transport("Unable to execute")), + } + } + Err(SendError::CannotReachDestination(destination, message)) + } +} + +/// This is a dummy router which accepts messages destined for `Remote` from `Local` +/// and then executes them in a context simulated to be like that of our `Remote`. Payment is +/// needed. struct ExecutingRouter(PhantomData<(Local, Remote, RemoteExporter)>); impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { let destination = destination.into(); if destination == Remote::get().relative_to(&Local::get()) { - // We now pretend that the message was delivered from the local chain - // Parachain(1000) to the relay-chain and is getting executed there. We need to - // configure the TestExecutor appropriately: + // We now pretend that the message was delivered from `Local` to `Remote`, and execute + // so we need to ensure that the `TestConfig` is set up properly for executing as + // though it is `Remote`. ExecutorUniversalLocation::set(Remote::get()); let origin = Local::get().relative_to(&Remote::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); + AllowPaidFrom::set(vec![origin.clone()]); set_exporter_override(RemoteExporter::export_xcm); // The we execute it: let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); - assert_matches!(outcome, Outcome::Complete(_)); - return Ok(()) + return match outcome { + Outcome::Complete(..) => Ok(()), + Outcome::Incomplete(..) => Err(SendError::Transport("Error executing")), + Outcome::Error(..) => Err(SendError::Transport("Unable to execute")), + } } Err(SendError::CannotReachDestination(destination, message)) } diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs new file mode 100644 index 000000000000..cb4165338bc3 --- /dev/null +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -0,0 +1,121 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! This test is when we're sending an XCM from a parachain whose relay-chain hosts a bridge to +//! another relay-chain. The destination of the XCM is within the global consensus of the +//! remote side of the bridge. +//! +//! The Relay-chain here requires payment by the parachain for use of the bridge. This is expressed +//! under the standard XCM weight and the weight pricing. + +use crate::mock::*; +use super::*; + +parameter_types! { + pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100)); + pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); + pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), MultiLocation::parent(), Some((Here, 100).into()))]; +} +type TheBridge = + TestBridge>; +type RelayExporter = HaulBlobExporter; +type LocalInnerRouter = ExecutingRouter; +type LocalBridgeRouter = SovereignPaidRemoteExporter< + NetworkExportTable, + LocalInnerRouter, + UniversalLocation, +>; +type LocalRouter = ( + LocalInnerRouter, + LocalBridgeRouter, +); + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) +/// /\ | +/// || | +/// || | +/// || | +/// Parachain(100) | +#[test] +fn sending_to_bridged_chain_works() { + let dest = (Parent, Parent, Remote::get()); + // Routing won't work if we don't have enough funds. + assert_eq!( + LocalRouter::send_xcm(dest.clone(), Xcm(vec![Trap(1)])), + Err(SendError::Transport("Error executing")), + ); + + // Initialize the local relay so that our parachain has funds to pay for export. + add_asset(to_account(Parachain(100)).unwrap(), (Here, 100)); + + let msg = Xcm(vec![Trap(1)]); + assert_eq!(::send_xcm(dest, msg), Ok(())); + assert_eq!(TheBridge::service(), 1); + assert_eq!( + take_received_remote_messages(), + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(100).into()), + Trap(1), + ]) + )] + ); + + // The export cost 50 weight units (and thus 50 units of balance). + assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 50).into()]); +} + +/// local | remote +/// | +/// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) +/// /\ | || +/// || | || +/// || | || +/// || | \/ +/// Parachain(100) | Parachain(100) +#[test] +fn sending_to_parachain_of_bridged_chain_works() { + let dest = (Parent, Parent, Remote::get(), Parachain(100)); + // Routing won't work if we don't have enough funds. + assert_eq!( + LocalRouter::send_xcm(dest.clone(), Xcm(vec![Trap(1)])), + Err(SendError::Transport("Error executing")), + ); + + // Initialize the local relay so that our parachain has funds to pay for export. + add_asset(to_account(Parachain(100)).unwrap(), (Here, 1000)); + + assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(TheBridge::service(), 1); + let expected = vec![( + Parachain(100).into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(100).into()), + Trap(1), + ]) + )]; + assert_eq!(take_received_remote_messages(), expected); + + // The export cost 50 weight units (and thus 50 units of balance). + assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 950).into()]); +} diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index 90d482a0b8df..2a3621e7917e 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -31,7 +31,7 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type LocalInnerRouter = ExecutingRouter; +type LocalInnerRouter = UnpaidExecutingRouter; type LocalBridgingRouter = UnpaidRemoteExporter< NetworkExportTable, LocalInnerRouter, diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index fe6c1a02f314..e6df8f112ba4 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -31,7 +31,7 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type LocalInnerRouter = ExecutingRouter; +type LocalInnerRouter = UnpaidExecutingRouter; type LocalBridgingRouter = UnpaidRemoteExporter< NetworkExportTable, LocalInnerRouter, diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index 8462d89140a1..9c593e383627 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -31,7 +31,7 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type LocalInnerRouter = ExecutingRouter; +type LocalInnerRouter = UnpaidExecutingRouter; type LocalBridgeRouter = UnpaidRemoteExporter< NetworkExportTable, LocalInnerRouter, diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 274f00c09f0b..b6d869d27ab3 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -175,8 +175,8 @@ impl TransactAsset for TestAssetTransactor { } } -pub fn to_account(l: MultiLocation) -> Result { - Ok(match l { +pub fn to_account(l: impl Into) -> Result { + Ok(match l.into() { // Siblings at 2000+id MultiLocation { parents: 1, interior: X1(Parachain(id)) } => 2000 + id as u64, // Accounts are their number @@ -187,7 +187,7 @@ pub fn to_account(l: MultiLocation) -> Result { MultiLocation { parents: 0, interior: Here } => 3000, // Parent at 3001 MultiLocation { parents: 1, interior: Here } => 3001, - _ => return Err(l), + l => return Err(l), }) } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 39a92f886a11..0c0cedf95de5 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -26,10 +26,10 @@ fn basic_setup_works() { &Parent.into(), )); - assert_eq!(to_account(X1(Parachain(1)).into()), Ok(1001)); - assert_eq!(to_account(X1(Parachain(50)).into()), Ok(1050)); - assert_eq!(to_account(MultiLocation::new(1, X1(Parachain(1)))), Ok(2001)); - assert_eq!(to_account(MultiLocation::new(1, X1(Parachain(50)))), Ok(2050)); + assert_eq!(to_account(Parachain(1)), Ok(1001)); + assert_eq!(to_account(Parachain(50)), Ok(1050)); + assert_eq!(to_account((Parent, Parachain(1))), Ok(2001)); + assert_eq!(to_account((Parent, Parachain(50))), Ok(2050)); assert_eq!( to_account(MultiLocation::new(0, X1(AccountIndex64 { index: 1, network: None }))), Ok(1), @@ -38,7 +38,7 @@ fn basic_setup_works() { to_account(MultiLocation::new(0, X1(AccountIndex64 { index: 42, network: None }))), Ok(42), ); - assert_eq!(to_account(Here.into()), Ok(3000)); + assert_eq!(to_account(Here), Ok(3000)); } #[test] From cc9c35f33c2de00ce5019645472538fddf67d0b7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 2 Feb 2022 19:31:24 +0000 Subject: [PATCH 34/57] Formatting --- .../src/bridging_tests/local_para_para.rs | 25 ++++++++--- .../src/bridging_tests/local_relay_relay.rs | 3 +- xcm/xcm-builder/src/bridging_tests/mod.rs | 29 ++++++++----- .../bridging_tests/paid_remote_relay_relay.rs | 9 ++-- .../src/bridging_tests/remote_para_para.rs | 30 ++++++------- .../remote_para_para_via_relay.rs | 43 ++++++------------- .../src/bridging_tests/remote_relay_relay.rs | 21 ++++----- xcm/xcm-builder/src/lib.rs | 4 +- xcm/xcm-builder/src/mock.rs | 12 +++--- xcm/xcm-builder/src/universal_exports.rs | 5 +-- 10 files changed, 86 insertions(+), 95 deletions(-) diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index 870eacb83ce4..a51f94587ddb 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -43,11 +43,14 @@ fn sending_to_bridged_chain_works() { assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), - vec![(Here.into(), Xcm(vec![ - UniversalOrigin(Local::get().into()), - DescendOrigin(Parachain(1).into()), - Trap(1), - ]))] + vec![( + Here.into(), + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1).into()), + Trap(1), + ]) + )] ); } @@ -66,7 +69,11 @@ fn sending_to_parachain_of_bridged_chain_works() { assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), - Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1).into()), + Trap(1), + ]), )]; assert_eq!(take_received_remote_messages(), expected); } @@ -86,7 +93,11 @@ fn sending_to_relay_chain_of_bridged_chain_works() { assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), - Xcm(vec![UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1).into()),Trap(1)]) + Xcm(vec![ + UniversalOrigin(Local::get().into()), + DescendOrigin(Parachain(1).into()), + Trap(1), + ]), )]; assert_eq!(take_received_remote_messages(), expected); } diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs index 8a147988ca8e..f402ec3b11ef 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -56,6 +56,7 @@ fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Remote::get(), Parachain(1000)); assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); - let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; + let expected = + vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; assert_eq!(take_received_remote_messages(), expected); } diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index eecbd54aa589..aa5760498312 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -16,19 +16,18 @@ //! Tests specific to the bridging primitives -use std::{cell::RefCell, marker::PhantomData}; +use crate::{mock::*, universal_exports::*}; use frame_support::{parameter_types, traits::Get}; +use std::{cell::RefCell, marker::PhantomData}; use xcm::prelude::*; use xcm_executor::XcmExecutor; -use crate::universal_exports::*; -use crate::mock::*; -mod local_relay_relay; mod local_para_para; -mod remote_relay_relay; +mod local_relay_relay; +mod paid_remote_relay_relay; mod remote_para_para; mod remote_para_para_via_relay; -mod paid_remote_relay_relay; +mod remote_relay_relay; parameter_types! { pub Local: NetworkId = ByUri(b"local".to_vec()); @@ -70,8 +69,12 @@ fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { /// This is a dummy router which accepts messages destined for `Remote` from `Local` /// and then executes them for free in a context simulated to be like that of our `Remote`. -struct UnpaidExecutingRouter(PhantomData<(Local, Remote, RemoteExporter)>); -impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { +struct UnpaidExecutingRouter( + PhantomData<(Local, Remote, RemoteExporter)>, +); +impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm + for UnpaidExecutingRouter +{ fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { let destination = destination.into(); if destination == Remote::get().relative_to(&Local::get()) { @@ -83,7 +86,8 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S AllowUnpaidFrom::set(vec![origin.clone()]); set_exporter_override(RemoteExporter::export_xcm); // The we execute it: - let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + let outcome = + XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); return match outcome { Outcome::Complete(..) => Ok(()), Outcome::Incomplete(..) => Err(SendError::Transport("Error executing")), @@ -98,7 +102,9 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S /// and then executes them in a context simulated to be like that of our `Remote`. Payment is /// needed. struct ExecutingRouter(PhantomData<(Local, Remote, RemoteExporter)>); -impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { +impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm + for ExecutingRouter +{ fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { let destination = destination.into(); if destination == Remote::get().relative_to(&Local::get()) { @@ -110,7 +116,8 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S AllowPaidFrom::set(vec![origin.clone()]); set_exporter_override(RemoteExporter::export_xcm); // The we execute it: - let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + let outcome = + XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); return match outcome { Outcome::Complete(..) => Ok(()), Outcome::Incomplete(..) => Err(SendError::Transport("Error executing")), diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs index cb4165338bc3..4db227a95383 100644 --- a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -21,8 +21,8 @@ //! The Relay-chain here requires payment by the parachain for use of the bridge. This is expressed //! under the standard XCM weight and the weight pricing. -use crate::mock::*; use super::*; +use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100)); @@ -40,10 +40,7 @@ type LocalBridgeRouter = SovereignPaidRemoteExporter< LocalInnerRouter, UniversalLocation, >; -type LocalRouter = ( - LocalInnerRouter, - LocalBridgeRouter, -); +type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); /// local | remote /// | @@ -112,7 +109,7 @@ fn sending_to_parachain_of_bridged_chain_works() { UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(100).into()), Trap(1), - ]) + ]), )]; assert_eq!(take_received_remote_messages(), expected); diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index 2a3621e7917e..80265f777f6c 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -18,8 +18,8 @@ //! bridge to a parachain from another global consensus. The destination of the XCM is within //! the global consensus of the remote side of the bridge. -use crate::mock::*; use super::*; +use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); @@ -31,16 +31,11 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type LocalInnerRouter = UnpaidExecutingRouter; -type LocalBridgingRouter = UnpaidRemoteExporter< - NetworkExportTable, - LocalInnerRouter, - UniversalLocation, ->; -type LocalRouter = ( - LocalInnerRouter, - LocalBridgingRouter, -); +type LocalInnerRouter = + UnpaidExecutingRouter; +type LocalBridgingRouter = + UnpaidRemoteExporter, LocalInnerRouter, UniversalLocation>; +type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); /// local | remote /// | @@ -53,7 +48,10 @@ type LocalRouter = ( #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), Ok(())); + assert_eq!( + ::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), + Ok(()) + ); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -87,8 +85,8 @@ fn sending_to_sibling_of_bridged_chain_works() { Xcm(vec![ UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) + Trap(1), + ]), )]; assert_eq!(take_received_remote_messages(), expected); } @@ -112,8 +110,8 @@ fn sending_to_relay_of_bridged_chain_works() { Xcm(vec![ UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) + Trap(1), + ]), )]; assert_eq!(take_received_remote_messages(), expected); } diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index e6df8f112ba4..867fc425e21a 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -18,8 +18,8 @@ //! bridge to a parachain from another global consensus. The destination of the XCM is within //! the global consensus of the remote side of the bridge. -use crate::mock::*; use super::*; +use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); @@ -31,16 +31,11 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type LocalInnerRouter = UnpaidExecutingRouter; -type LocalBridgingRouter = UnpaidRemoteExporter< - NetworkExportTable, - LocalInnerRouter, - UniversalLocation, ->; -type LocalRouter = ( - LocalInnerRouter, - LocalBridgingRouter, -); +type LocalInnerRouter = + UnpaidExecutingRouter; +type LocalBridgingRouter = + UnpaidRemoteExporter, LocalInnerRouter, UniversalLocation>; +type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); /// local | remote /// | @@ -53,17 +48,14 @@ type LocalRouter = ( #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Remote::get(), Parachain(1)), msg), Ok(())); + assert_eq!( + ::send_xcm((Parent, Remote::get(), Parachain(1)), msg), + Ok(()) + ); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), - vec![( - Here.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1) - ]) - )] + vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))] ); } @@ -83,10 +75,7 @@ fn sending_to_sibling_of_bridged_chain_works() { assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1) - ]) + Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]), )]; assert_eq!(take_received_remote_messages(), expected); } @@ -105,12 +94,6 @@ fn sending_to_relay_of_bridged_chain_works() { let dest = (Parent, Remote::get()); assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); - let expected = vec![( - Parent.into(), - Xcm(vec![ - UniversalOrigin(Local::get().into()), - Trap(1) - ]) - )]; + let expected = vec![(Parent.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; assert_eq!(take_received_remote_messages(), expected); } diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index 9c593e383627..ba2612e07ca3 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -18,8 +18,8 @@ //! another relay-chain. The destination of the XCM is within the global consensus of the //! remote side of the bridge. -use crate::mock::*; use super::*; +use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); @@ -31,16 +31,11 @@ parameter_types! { type TheBridge = TestBridge>; type RelayExporter = HaulBlobExporter; -type LocalInnerRouter = UnpaidExecutingRouter; -type LocalBridgeRouter = UnpaidRemoteExporter< - NetworkExportTable, - LocalInnerRouter, - UniversalLocation, ->; -type LocalRouter = ( - LocalInnerRouter, - LocalBridgeRouter, -); +type LocalInnerRouter = + UnpaidExecutingRouter; +type LocalBridgeRouter = + UnpaidRemoteExporter, LocalInnerRouter, UniversalLocation>; +type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); /// local | remote /// | @@ -86,8 +81,8 @@ fn sending_to_parachain_of_bridged_chain_works() { Xcm(vec![ UniversalOrigin(Local::get().into()), DescendOrigin(Parachain(1000).into()), - Trap(1) - ]) + Trap(1), + ]), )]; assert_eq!(take_received_remote_messages(), expected); } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 4bf9ed6b3cba..506ee8d8f37f 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -20,12 +20,12 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod bridging_tests; #[cfg(test)] mod mock; #[cfg(test)] mod tests; -#[cfg(test)] -mod bridging_tests; #[cfg(feature = "std")] pub mod test_utils; diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index b6d869d27ab3..5b2326f77ffe 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -136,11 +136,13 @@ impl ExportXcm for TestMessageExporter { dest: impl Into, msg: opaque::Xcm, ) -> SendResult { - EXPORTER_OVERRIDE.with(|e| if let Some(ref f) = &*e.borrow() { - f(network, channel, dest.into(), msg) - } else { - EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); - Ok(()) + EXPORTER_OVERRIDE.with(|e| { + if let Some(ref f) = &*e.borrow() { + f(network, channel, dest.into(), msg) + } else { + EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); + Ok(()) + } }) } } diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 161f6a6b8f9f..8d62fd3bb06f 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -113,10 +113,7 @@ impl)>>> ExporterFor _: &InteriorMultiLocation, _: &Xcm<()>, ) -> Option<(MultiLocation, Option)> { - T::get() - .into_iter() - .find(|(ref j, ..)| j == network) - .map(|(_, l, p)| (l, p)) + T::get().into_iter().find(|(ref j, ..)| j == network).map(|(_, l, p)| (l, p)) } } From ac2418497a257bd39ebd321ec96ed275a372b5e4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 4 Feb 2022 12:39:23 +0000 Subject: [PATCH 35/57] Tests --- xcm/src/v3/mod.rs | 2 +- xcm/xcm-builder/src/barriers.rs | 16 +++--- xcm/xcm-builder/src/lib.rs | 2 +- xcm/xcm-builder/src/mock.rs | 26 ++++++++-- xcm/xcm-builder/src/tests.rs | 90 ++++++++++++++++++++++++++++++++- xcm/xcm-executor/src/lib.rs | 10 ++-- 6 files changed, 127 insertions(+), 19 deletions(-) diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 9bfede998da2..a59846efd337 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -368,7 +368,7 @@ pub enum Instruction { TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, xcm: Xcm<()> }, /// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed - /// by the kind of origin `origin_type`. + /// by the kind of origin `origin_kind`. /// /// The Transact Status Register is set according to the result of dispatching the call. /// diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index a1febc537cd6..88f08d6b7b99 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -25,8 +25,8 @@ use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; use xcm::latest::{ Instruction::{self, *}, - Junction, Junctions, MultiLocation, - WeightLimit::*, + Junction, Junctions, Junctions::X1, MultiLocation, + WeightLimit::*, InteriorMultiLocation, }; use xcm_executor::traits::{OnResponse, ShouldExecute}; @@ -145,9 +145,9 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro /// response from. For example, even if an origin appeared in the `AllowedSubscribers` list, we /// would ignore this rule if it began with origin mutators and they changed the origin to something /// which was not on the list. -pub struct WithComputedOrigin(PhantomData<(InnerBarrier, MaxPrefixes)>); -impl> ShouldExecute - for WithComputedOrigin +pub struct WithComputedOrigin(PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>); +impl, MaxPrefixes: Get> ShouldExecute + for WithComputedOrigin { fn should_execute( origin: &MultiLocation, @@ -169,8 +169,10 @@ impl> ShouldExecute // invalid UniversalOrigin. while skipped < MaxPrefixes::get() as usize { match instructions.get(skipped) { - Some(UniversalOrigin(j)) => { - actual_origin = j.clone().into(); + Some(UniversalOrigin(new_global)) => { + // Note the origin is *relative to local consensus*! So we need to escape local + // consensus with the `parents` before diving in into the `universal_location`. + actual_origin = X1(new_global.clone()).relative_to(&LocalUniversal::get()); }, Some(DescendOrigin(j)) => { actual_origin.append_with(j.clone()).map_err(|_| ())?; diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 506ee8d8f37f..20f57496e696 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -47,7 +47,7 @@ pub use origin_conversion::{ mod barriers; pub use barriers::{ AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - AllowUnpaidExecutionFrom, IsChildSystemParachain, TakeWeightCredit, + AllowUnpaidExecutionFrom, IsChildSystemParachain, TakeWeightCredit, WithComputedOrigin, }; mod currency_adapter; diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 5b2326f77ffe..740e184f9a4d 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -118,6 +118,7 @@ pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm pub fn set_exporter_override(f: fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> SendResult) { EXPORTER_OVERRIDE.with(|x| x.replace(Some(f))); } +#[allow(dead_code)] pub fn clear_exporter_override() { EXPORTER_OVERRIDE.with(|x| x.replace(None)); } @@ -189,7 +190,18 @@ pub fn to_account(l: impl Into) -> Result { MultiLocation { parents: 0, interior: Here } => 3000, // Parent at 3001 MultiLocation { parents: 1, interior: Here } => 3001, - l => return Err(l), + l => { + // Is it a foreign-consensus? + let uni = ExecutorUniversalLocation::get(); + if l.parents as usize != uni.len() { + return Err(l) + } + match l.first_interior() { + Some(GlobalConsensus(Kusama)) => 4000, + Some(GlobalConsensus(Polkadot)) => 4001, + _ => return Err(l), + } + }, }) } @@ -225,10 +237,13 @@ pub fn add_reserve(from: MultiLocation, asset: MultiAssetFilter) { pub fn add_teleporter(from: MultiLocation, asset: MultiAssetFilter) { IS_TELEPORTER.with(|r| r.borrow_mut().entry(from).or_default().push(asset)); } -#[allow(dead_code)] -pub fn add_universal_alias(bridge: MultiLocation, consensus: Junction) { - UNIVERSAL_ALIASES.with(|r| r.borrow_mut().insert((bridge, consensus))); +pub fn add_universal_alias(bridge: impl Into, consensus: impl Into) { + UNIVERSAL_ALIASES.with(|r| r.borrow_mut().insert((bridge.into(), consensus.into()))); +} +pub fn clear_universal_aliases() { + UNIVERSAL_ALIASES.with(|r| r.replace(Default::default())); } + pub struct TestIsReserve; impl FilterAssetLocation for TestIsReserve { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { @@ -300,7 +315,8 @@ pub fn response(query_id: u64) -> Option { } parameter_types! { - pub static ExecutorUniversalLocation: InteriorMultiLocation = X1(Parachain(42)); + pub static ExecutorUniversalLocation: InteriorMultiLocation + = (ByUri(b"local"[..].into()), Parachain(42)).into(); pub UnitWeightCost: Weight = 10; } parameter_types! { diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 0c0cedf95de5..06082e64d953 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use super::{mock::*, test_utils::*, *}; -use frame_support::{assert_err, weights::constants::WEIGHT_PER_SECOND}; +use frame_support::{assert_err, weights::constants::WEIGHT_PER_SECOND, traits::ConstU32}; use xcm_executor::{traits::*, Config, XcmExecutor}; #[test] @@ -75,6 +75,52 @@ fn take_weight_credit_barrier_should_work() { assert_eq!(weight_credit, 0); } +#[test] +fn computed_origin_should_work() { + let mut message = Xcm::<()>(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + DescendOrigin(Parachain(100).into()), + DescendOrigin(PalletInstance(69).into()), + WithdrawAsset((Parent, 100).into()), + BuyExecution { fees: (Parent, 100).into(), weight_limit: Limited(100) }, + TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }, + ]); + + AllowPaidFrom::set(vec![(Parent, Parent, GlobalConsensus(Kusama), Parachain(100), PalletInstance(69)).into()]); + + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + message.inner_mut(), + 100, + &mut 0, + ); + assert_eq!(r, Err(())); + + let r = WithComputedOrigin::< + AllowTopLevelPaidExecutionFrom::>, + ExecutorUniversalLocation, + ConstU32<2>, + >::should_execute( + &Parent.into(), + message.inner_mut(), + 100, + &mut 0, + ); + assert_eq!(r, Err(())); + + let r = WithComputedOrigin::< + AllowTopLevelPaidExecutionFrom::>, + ExecutorUniversalLocation, + ConstU32<5>, + >::should_execute( + &Parent.into(), + message.inner_mut(), + 100, + &mut 0, + ); + assert_eq!(r, Ok(())); +} + #[test] fn allow_unpaid_should_work() { let mut message = @@ -192,6 +238,48 @@ fn transfer_should_work() { assert_eq!(sent_xcm(), vec![]); } +#[test] +fn universal_origin_should_work() { + AllowUnpaidFrom::set(vec![X1(Parachain(1)).into(), X1(Parachain(2)).into()]); + clear_universal_aliases(); + // Parachain 1 may represent Kusama to us + add_universal_alias(Parachain(1), Kusama); + // Parachain 2 may represent Polkadot to us + add_universal_alias(Parachain(2), Polkadot); + + let r = XcmExecutor::::execute_xcm( + Parachain(2), + Xcm(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }, + ]), + 50, + ); + assert_eq!(r, Outcome::Incomplete(10, XcmError::InvalidLocation)); + + let r = XcmExecutor::::execute_xcm( + Parachain(1), + Xcm(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }, + ]), + 50, + ); + assert_eq!(r, Outcome::Incomplete(20, XcmError::NotWithdrawable)); + + add_asset(4000, (Parent, 100)); + let r = XcmExecutor::::execute_xcm( + Parachain(1), + Xcm(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }, + ]), + 50, + ); + assert_eq!(r, Outcome::Complete(20)); + assert_eq!(assets(4000), vec![]); +} + #[test] fn export_message_should_work() { // Bridge chain (assumed to be Relay) lets Parachain #1 have message execution for free. diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index c41b064597e6..8c678b51657c 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -569,13 +569,15 @@ impl XcmExecutor { self.transact_status = Default::default(); Ok(()) }, - UniversalOrigin(j) => { + UniversalOrigin(new_global) => { let universal_location = Config::LocationInverter::universal_location(); - ensure!(universal_location.first() != Some(&j), XcmError::InvalidLocation); + ensure!(universal_location.first() != Some(&new_global), XcmError::InvalidLocation); let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); - let new_origin = AncestorThen(universal_location.len() as u8, X1(j.clone())).into(); - let ok = Config::UniversalAliases::contains(&(origin, j)); + let origin_xform = (origin, new_global); + let ok = Config::UniversalAliases::contains(&origin_xform); ensure!(ok, XcmError::InvalidLocation); + let (_, new_global) = origin_xform; + let new_origin = X1(new_global).relative_to(&universal_location); self.origin = Some(new_origin); Ok(()) }, From b90d0419a49e9723a629d23491bfb2cc5f5c661c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 4 Feb 2022 13:02:39 +0000 Subject: [PATCH 36/57] Spelling --- scripts/gitlab/lingua.dic | 2 ++ xcm/xcm-builder/src/bridging_tests/local_para_para.rs | 6 ++++++ xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs | 4 ++++ .../src/bridging_tests/paid_remote_relay_relay.rs | 5 ++++- xcm/xcm-builder/src/bridging_tests/remote_para_para.rs | 9 ++++++--- .../src/bridging_tests/remote_para_para_via_relay.rs | 9 ++++++--- xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs | 5 ++++- xcm/xcm-builder/src/universal_exports.rs | 4 ++-- 8 files changed, 34 insertions(+), 10 deletions(-) diff --git a/scripts/gitlab/lingua.dic b/scripts/gitlab/lingua.dic index b239dac6d4c1..031018e952a6 100644 --- a/scripts/gitlab/lingua.dic +++ b/scripts/gitlab/lingua.dic @@ -160,6 +160,7 @@ MQC/SM msg multisig/S multivalidator/SM +mutators mutex natively NFA @@ -284,6 +285,7 @@ UDP UI unassign unconcluded +unexpectable unfinalize/B unfinalized union/MSG diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index a51f94587ddb..efea3a06e073 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -28,6 +28,7 @@ type TheBridge = TestBridge>; type Router = LocalUnpaidExporter, UniversalLocation>; +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -36,6 +37,7 @@ type Router = LocalUnpaidExporter, Universal /// | /// | /// Parachain(1) ===> Parachain(1) +/// ``` #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); @@ -54,6 +56,7 @@ fn sending_to_bridged_chain_works() { ); } +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -62,6 +65,7 @@ fn sending_to_bridged_chain_works() { /// | /// | /// Parachain(1) ===> Parachain(1) ==> Parachain(1000) +/// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)); @@ -78,6 +82,7 @@ fn sending_to_parachain_of_bridged_chain_works() { assert_eq!(take_received_remote_messages(), expected); } +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -86,6 +91,7 @@ fn sending_to_parachain_of_bridged_chain_works() { /// | || /// | || /// Parachain(1) ===> Parachain(1) +/// ``` #[test] fn sending_to_relay_chain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get()); diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs index f402ec3b11ef..732cb0ddecae 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -28,10 +28,12 @@ type TheBridge = TestBridge>; type Router = LocalUnpaidExporter, UniversalLocation>; +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) /// | +/// ``` #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); @@ -43,6 +45,7 @@ fn sending_to_bridged_chain_works() { ); } +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) @@ -51,6 +54,7 @@ fn sending_to_bridged_chain_works() { /// | || /// | \/ /// | Parachain(1000) +/// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Remote::get(), Parachain(1000)); diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs index 4db227a95383..757c8ca09705 100644 --- a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -22,7 +22,6 @@ //! under the standard XCM weight and the weight pricing. use super::*; -use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100)); @@ -42,6 +41,7 @@ type LocalBridgeRouter = SovereignPaidRemoteExporter< >; type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) @@ -50,6 +50,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); /// || | /// || | /// Parachain(100) | +/// ``` #[test] fn sending_to_bridged_chain_works() { let dest = (Parent, Parent, Remote::get()); @@ -81,6 +82,7 @@ fn sending_to_bridged_chain_works() { assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 50).into()]); } +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) @@ -89,6 +91,7 @@ fn sending_to_bridged_chain_works() { /// || | || /// || | \/ /// Parachain(100) | Parachain(100) +/// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(100)); diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index 80265f777f6c..60fdba6a5397 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -19,7 +19,6 @@ //! the global consensus of the remote side of the bridge. use super::*; -use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); @@ -37,6 +36,7 @@ type LocalBridgingRouter = UnpaidRemoteExporter, LocalInnerRouter, UniversalLocation>; type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -45,6 +45,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); /// | /// | /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) +/// ``` #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); @@ -66,7 +67,7 @@ fn sending_to_bridged_chain_works() { ); } -/// +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -75,6 +76,7 @@ fn sending_to_bridged_chain_works() { /// | /// | /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) ===> Parachain(1000) +/// ``` #[test] fn sending_to_sibling_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)); @@ -91,7 +93,7 @@ fn sending_to_sibling_of_bridged_chain_works() { assert_eq!(take_received_remote_messages(), expected); } -/// +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -100,6 +102,7 @@ fn sending_to_sibling_of_bridged_chain_works() { /// | || /// | || /// Parachain(1000) ===> Parachain(1) ===> Parachain(1) +/// ``` #[test] fn sending_to_relay_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get()); diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index 867fc425e21a..dfe4ef047d84 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -19,7 +19,6 @@ //! the global consensus of the remote side of the bridge. use super::*; -use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); @@ -37,6 +36,7 @@ type LocalBridgingRouter = UnpaidRemoteExporter, LocalInnerRouter, UniversalLocation>; type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -45,6 +45,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); /// || | /// \/ | /// Parachain(1) ===> Parachain(1) +/// ``` #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); @@ -59,7 +60,7 @@ fn sending_to_bridged_chain_works() { ); } -/// +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -68,6 +69,7 @@ fn sending_to_bridged_chain_works() { /// || | /// \/ | /// Parachain(1) ===> Parachain(1) ===> Parachain(1000) +/// ``` #[test] fn sending_to_sibling_of_bridged_chain_works() { let dest = (Parent, Remote::get(), Parachain(1000)); @@ -80,7 +82,7 @@ fn sending_to_sibling_of_bridged_chain_works() { assert_eq!(take_received_remote_messages(), expected); } -/// +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) | GlobalConsensus(Remote::get()) @@ -89,6 +91,7 @@ fn sending_to_sibling_of_bridged_chain_works() { /// || | || /// \/ | || /// Parachain(1) ===> Parachain(1) +/// ``` #[test] fn sending_to_relay_of_bridged_chain_works() { let dest = (Parent, Remote::get()); diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index ba2612e07ca3..fa7bdf2c5acb 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -19,7 +19,6 @@ //! remote side of the bridge. use super::*; -use crate::mock::*; parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000)); @@ -37,6 +36,7 @@ type LocalBridgeRouter = UnpaidRemoteExporter, LocalInnerRouter, UniversalLocation>; type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) @@ -45,6 +45,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); /// || | /// || | /// Parachain(1000) | +/// ``` #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); @@ -63,6 +64,7 @@ fn sending_to_bridged_chain_works() { ); } +/// ``` nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) @@ -71,6 +73,7 @@ fn sending_to_bridged_chain_works() { /// || | || /// || | \/ /// Parachain(1000) | Parachain(1000) +/// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)); diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 8d62fd3bb06f..c95523a20eee 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -44,8 +44,8 @@ fn ensure_is_remote( Ok((remote_net, remote_dest, local_net, local_loc)) } -/// Implementation of `SendXcm` which uses the given `ExportXcm` impl in order to forward the -/// message over a bridge. +/// Implementation of `SendXcm` which uses the given `ExportXcm` implementation in order to forward +/// the message over a bridge. /// /// The actual message forwarded over the bridge is prepended with `UniversalOrigin` and /// `DescendOrigin` in order to ensure that the message is executed with this Origin. From 7310377e1003e8f44a9fe03ead74dd22f81b8c1d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 4 Feb 2022 13:03:00 +0000 Subject: [PATCH 37/57] Formatting --- xcm/xcm-builder/src/barriers.rs | 17 ++++++++++++----- xcm/xcm-builder/src/tests.rs | 29 +++++++++++++---------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index 88f08d6b7b99..8a29a3246617 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -25,8 +25,10 @@ use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; use xcm::latest::{ Instruction::{self, *}, - Junction, Junctions, Junctions::X1, MultiLocation, - WeightLimit::*, InteriorMultiLocation, + InteriorMultiLocation, Junction, Junctions, + Junctions::X1, + MultiLocation, + WeightLimit::*, }; use xcm_executor::traits::{OnResponse, ShouldExecute}; @@ -145,9 +147,14 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro /// response from. For example, even if an origin appeared in the `AllowedSubscribers` list, we /// would ignore this rule if it began with origin mutators and they changed the origin to something /// which was not on the list. -pub struct WithComputedOrigin(PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>); -impl, MaxPrefixes: Get> ShouldExecute - for WithComputedOrigin +pub struct WithComputedOrigin( + PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>, +); +impl< + InnerBarrier: ShouldExecute, + LocalUniversal: Get, + MaxPrefixes: Get, + > ShouldExecute for WithComputedOrigin { fn should_execute( origin: &MultiLocation, diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 06082e64d953..b9611fad829d 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use super::{mock::*, test_utils::*, *}; -use frame_support::{assert_err, weights::constants::WEIGHT_PER_SECOND, traits::ConstU32}; +use frame_support::{assert_err, traits::ConstU32, weights::constants::WEIGHT_PER_SECOND}; use xcm_executor::{traits::*, Config, XcmExecutor}; #[test] @@ -86,7 +86,14 @@ fn computed_origin_should_work() { TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }, ]); - AllowPaidFrom::set(vec![(Parent, Parent, GlobalConsensus(Kusama), Parachain(100), PalletInstance(69)).into()]); + AllowPaidFrom::set(vec![( + Parent, + Parent, + GlobalConsensus(Kusama), + Parachain(100), + PalletInstance(69), + ) + .into()]); let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &Parent.into(), @@ -97,27 +104,17 @@ fn computed_origin_should_work() { assert_eq!(r, Err(())); let r = WithComputedOrigin::< - AllowTopLevelPaidExecutionFrom::>, + AllowTopLevelPaidExecutionFrom>, ExecutorUniversalLocation, ConstU32<2>, - >::should_execute( - &Parent.into(), - message.inner_mut(), - 100, - &mut 0, - ); + >::should_execute(&Parent.into(), message.inner_mut(), 100, &mut 0); assert_eq!(r, Err(())); let r = WithComputedOrigin::< - AllowTopLevelPaidExecutionFrom::>, + AllowTopLevelPaidExecutionFrom>, ExecutorUniversalLocation, ConstU32<5>, - >::should_execute( - &Parent.into(), - message.inner_mut(), - 100, - &mut 0, - ); + >::should_execute(&Parent.into(), message.inner_mut(), 100, &mut 0); assert_eq!(r, Ok(())); } From d5b6f273c6f25ad74ea5aa4a018dc98b6e7dab57 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 4 Feb 2022 16:54:25 +0000 Subject: [PATCH 38/57] Fees and refactoring --- runtime/common/src/xcm_sender.rs | 39 +++--- runtime/test-runtime/src/xcm_config.rs | 2 +- xcm/pallet-xcm-benchmarks/src/mock.rs | 2 +- xcm/pallet-xcm/src/lib.rs | 14 +- xcm/pallet-xcm/src/mock.rs | 9 +- xcm/src/lib.rs | 4 +- xcm/src/v0/junction.rs | 1 - xcm/src/v3/junction.rs | 15 +-- xcm/src/v3/mod.rs | 10 +- xcm/src/v3/traits.rs | 100 +++++++++------ .../src/bridging_tests/local_para_para.rs | 11 +- .../src/bridging_tests/local_relay_relay.rs | 6 +- xcm/xcm-builder/src/bridging_tests/mod.rs | 91 ++++++------- .../bridging_tests/paid_remote_relay_relay.rs | 12 +- .../src/bridging_tests/remote_para_para.rs | 10 +- .../remote_para_para_via_relay.rs | 10 +- .../src/bridging_tests/remote_relay_relay.rs | 8 +- xcm/xcm-builder/src/mock.rs | 24 ++-- xcm/xcm-builder/src/universal_exports.rs | 121 ++++++++++-------- xcm/xcm-builder/tests/mock/mod.rs | 5 +- xcm/xcm-executor/src/lib.rs | 19 +-- xcm/xcm-executor/src/traits/export.rs | 51 ++++++-- xcm/xcm-executor/src/traits/mod.rs | 2 +- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 2 +- xcm/xcm-simulator/src/lib.rs | 46 +++---- 26 files changed, 345 insertions(+), 271 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 2d75edfd4571..9c59890b8e11 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -20,6 +20,7 @@ use parity_scale_codec::Encode; use runtime_parachains::{configuration, dmp}; use sp_std::marker::PhantomData; use xcm::latest::prelude::*; +use SendError::*; /// XCM sender for relay chain. It only sends downward message. pub struct ChildParachainRouter(PhantomData<(T, W)>); @@ -27,23 +28,25 @@ pub struct ChildParachainRouter(PhantomData<(T, W)>); impl SendXcm for ChildParachainRouter { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { - let dest = dest.into(); - match dest { - MultiLocation { parents: 0, interior: X1(Parachain(id)) } => { - // Downward message passing. - let versioned_xcm = - W::wrap_version(&dest, msg).map_err(|()| SendError::DestinationUnsupported)?; - let config = >::config(); - >::queue_downward_message( - &config, - id.into(), - versioned_xcm.encode(), - ) - .map_err(Into::::into)?; - Ok(()) - }, - dest => Err(SendError::CannotReachDestination(dest, msg)), - } + fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + let d = dest.take().ok_or(MissingArgument)?; + let id = if let MultiLocation { parents: 0, interior: X1(Parachain(id)) } = &d { + *id + } else { + *dest = Some(d); + return Err(CannotReachDestination); + }; + + // Downward message passing. + let xcm = msg.take().ok_or(MissingArgument)?; + let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?; + let config = >::config(); + >::queue_downward_message( + &config, + id.into(), + versioned_xcm.encode(), + ) + .map_err(Into::::into)?; + Ok(()) } } diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index c1a2eb1e34fa..bbbea1dedee6 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -41,7 +41,7 @@ pub type LocalOriginToLocation = ( pub struct DoNothingRouter; impl SendXcm for DoNothingRouter { - fn send_xcm(_dest: impl Into, _msg: Xcm<()>) -> SendResult { + fn send_xcm(_dest: &mut Option, _msg: &mut Option>) -> SendResult { Ok(()) } } diff --git a/xcm/pallet-xcm-benchmarks/src/mock.rs b/xcm/pallet-xcm-benchmarks/src/mock.rs index f60ee5060e16..4d23175bdec3 100644 --- a/xcm/pallet-xcm-benchmarks/src/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/mock.rs @@ -21,7 +21,7 @@ use xcm_executor::traits::FilterAssetLocation; // An xcm sender/receiver akin to > /dev/null pub struct DevNull; impl xcm::opaque::latest::SendXcm for DevNull { - fn send_xcm(_: impl Into, _: Xcm<()>) -> SendResult { + fn send_xcm(_: &mut Option, _: &mut Option>) -> SendResult { Ok(()) } } diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index a1b942906fbd..e53b0e245b58 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -557,7 +557,7 @@ pub mod pallet { let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; Self::send_xcm(interior, dest.clone(), message.clone()).map_err(|e| match e { - SendError::CannotReachDestination(..) => Error::::Unreachable, + SendError::CannotReachDestination => Error::::Unreachable, _ => Error::::SendFailure, })?; Self::deposit_event(Event::Sent(origin_location, dest, message)); @@ -1053,7 +1053,7 @@ pub mod pallet { let response = Response::Version(xcm_version); let message = Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]); - let event = match T::XcmRouter::send_xcm(new_key.clone(), message) { + let event = match send_xcm::(new_key.clone(), message) { Ok(()) => { let value = (query_id, max_weight, xcm_version); VersionNotifyTargets::::insert(XCM_VERSION, key, value); @@ -1104,7 +1104,7 @@ pub mod pallet { max_weight, querier: None, }]); - let event = match T::XcmRouter::send_xcm(new_key.clone(), message) { + let event = match send_xcm::(new_key.clone(), message) { Ok(()) => { VersionNotifyTargets::::insert( XCM_VERSION, @@ -1140,7 +1140,7 @@ pub mod pallet { }); // TODO #3735: Correct weight. let instruction = SubscribeVersion { query_id, max_response_weight: 0 }; - T::XcmRouter::send_xcm(dest, Xcm(vec![instruction]))?; + send_xcm::(dest, Xcm(vec![instruction]))?; VersionNotifiers::::insert(XCM_VERSION, &versioned_dest, query_id); let query_status = QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false }; @@ -1154,7 +1154,7 @@ pub mod pallet { let versioned_dest = LatestVersionedMultiLocation(&dest); let query_id = VersionNotifiers::::take(XCM_VERSION, versioned_dest) .ok_or(XcmError::InvalidLocation)?; - T::XcmRouter::send_xcm(dest.clone(), Xcm(vec![UnsubscribeVersion]))?; + send_xcm::(dest.clone(), Xcm(vec![UnsubscribeVersion]))?; Queries::::remove(query_id); Ok(()) } @@ -1172,7 +1172,7 @@ pub mod pallet { message.0.insert(0, DescendOrigin(interior)) }; log::trace!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message); - T::XcmRouter::send_xcm(dest, message) + send_xcm::(dest, message) } pub fn check_account() -> T::AccountId { @@ -1369,7 +1369,7 @@ pub mod pallet { let xcm_version = T::AdvertisedXcmVersion::get(); let response = Response::Version(xcm_version); let instruction = QueryResponse { query_id, response, max_weight, querier: None }; - T::XcmRouter::send_xcm(dest.clone(), Xcm(vec![instruction]))?; + send_xcm::(dest.clone(), Xcm(vec![instruction]))?; let value = (query_id, max_weight, xcm_version); VersionNotifyTargets::::insert(XCM_VERSION, versioned_dest, value); diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 99928e98e830..0128f0fb3ec2 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -156,16 +156,17 @@ pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { /// Sender that never returns error, always sends pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest.into(), msg))); + fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + let pair = (dest.take().unwrap(), msg.take().unwrap()); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } } /// Sender that returns error if `X8` junction and stops routing pub struct TestSendXcmErrX8; impl SendXcm for TestSendXcmErrX8 { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { - let dest = dest.into(); + fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + let (dest, msg) = (dest.take().unwrap(), msg.take().unwrap()); if dest.len() == 8 { Err(SendError::Transport("Destination location full")) } else { diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index a7360bb8cd51..aa48165bf73e 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -621,13 +621,13 @@ pub mod opaque { pub use crate::v1::opaque::{Order, Xcm}; } pub mod v2 { - // Everything from v1 + // Everything from v2 pub use crate::v2::*; // Then override with the opaque types in v2 pub use crate::v2::opaque::{Instruction, Xcm}; } pub mod v3 { - // Everything from v2 + // Everything from v3 pub use crate::v3::*; // Then override with the opaque types in v3 pub use crate::v3::opaque::{Instruction, Xcm}; diff --git a/xcm/src/v0/junction.rs b/xcm/src/v0/junction.rs index 0d871d5179dc..7ab3f5b1ad9f 100644 --- a/xcm/src/v0/junction.rs +++ b/xcm/src/v0/junction.rs @@ -41,7 +41,6 @@ impl TryInto for Option { use NewNetworkId::*; Ok(match self { None => NetworkId::Any, - Some(ByUri(name)) => NetworkId::Named(name), Some(Polkadot) => NetworkId::Polkadot, Some(Kusama) => NetworkId::Kusama, _ => return Err(()), diff --git a/xcm/src/v3/junction.rs b/xcm/src/v3/junction.rs index e29043594e4c..030f1b786881 100644 --- a/xcm/src/v3/junction.rs +++ b/xcm/src/v3/junction.rs @@ -30,19 +30,8 @@ use scale_info::TypeInfo; /// /// Maintenance note: Networks with global consensus and which are practically bridgeable within the /// Polkadot ecosystem are given preference over explicit naming in this enumeration. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] pub enum NetworkId { - /// The network identified by a globally recognized URI. Best practice is to use the canonical - /// server name of the primary website providing information on the network. E.g.: - /// - /// - `bitcoin.org` - /// - `ethereum.org` - /// - `polkadot.network` - /// - /// In general (and like the above three networks) networks should either be introduced into - /// this list or referenced by their genesis hash and/or fork properties (in the case that two - /// networks exist with the same genesis hash). - ByUri(Vec), /// Network specified by the first 32 bytes of its genesis block. ByGenesis([u8; 32]), /// Network defined by the first 32-bytes of the hash and number of some block it contains. @@ -72,7 +61,7 @@ impl From for Option { use OldNetworkId::*; match old { Any => None, - Named(name) => Some(NetworkId::ByUri(name)), + Named(_) => None, Polkadot => Some(NetworkId::Polkadot), Kusama => Some(NetworkId::Kusama), } diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index a59846efd337..b43e704d8a81 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -44,7 +44,8 @@ pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; pub use traits::{ - Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, SendResult, SendXcm, Weight, + Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, SendResult, SendCostResult, + SendXcm, Weight, send_xcm, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -178,7 +179,8 @@ pub mod prelude { MultiAssets, MultiLocation, NetworkId::{self, *}, OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, - QueryResponseInfo, Response, Result as XcmResult, SendError, SendResult, SendXcm, + QueryResponseInfo, Response, Result as XcmResult, SendError, SendCostResult, SendResult, + SendXcm, send_xcm, WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, @@ -781,9 +783,11 @@ pub enum Instruction { /// Errors: *Fallible*. UniversalOrigin(Junction), - /// Send a message onwards beyond the local consensus system. + /// Send a message on to Non-Local Consensus system. /// /// This will tend to utilize some extra-consensus mechanism, the obvious one being a bridge. + /// A fee may be charged; this may be determined based on the contents of `xcm`. It will be + /// taken from the Holding register. /// /// - `network`: The remote consensus system to which the message should be exported. /// - `destination`: The location relative to the remote consensus system to which the message diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 3749b74a4e4f..9f9432e4deec 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -167,11 +167,13 @@ impl TryFrom for Error { impl From for Error { fn from(e: SendError) -> Self { match e { - SendError::CannotReachDestination(..) | SendError::Unroutable => Error::Unroutable, + SendError::CannotReachDestination + | SendError::Unroutable + | SendError::MissingArgument + => Error::Unroutable, SendError::Transport(s) => Error::Transport(s), SendError::DestinationUnsupported => Error::DestinationUnsupported, SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, - SendError::CannotReachNetwork(..) => Error::Unroutable, } } } @@ -295,8 +297,8 @@ pub enum SendError { /// The message and destination combination was not recognized as being reachable. /// /// This is not considered fatal: if there are alternative transport routes available, then - /// they may be attempted. For this reason, the destination and message are contained. - CannotReachDestination(MultiLocation, Xcm<()>), + /// they may be attempted. + CannotReachDestination, /// Destination is routable, but there is some issue with the transport mechanism. This is /// considered fatal. /// A human-readable explanation of the specific issue is provided. @@ -309,16 +311,16 @@ pub enum SendError { /// Message could not be sent due to its size exceeding the maximum allowed by the transport /// layer. ExceedsMaxMessageSize, - /// The network was not recognized as being reachable. - /// - /// This is not considered fatal: if there are alternative transport routes available, then - /// they may be attempted. For this reason, the network, destination and message are contained. - CannotReachNetwork(NetworkId, InteriorMultiLocation, Xcm<()>), + /// A needed argument is `None` when it should be `Some`. + MissingArgument, } /// Result value when attempting to send an XCM message. pub type SendResult = result::Result<(), SendError>; +/// Result value when attempting to determine the cost for sending an XCM message. +pub type SendCostResult = result::Result; + /// Utility for sending an XCM message. /// /// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return @@ -334,16 +336,17 @@ pub type SendResult = result::Result<(), SendError>; /// /// A sender that only passes the message through and does nothing. /// struct Sender1; /// impl SendXcm for Sender1 { -/// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// return Err(SendError::CannotReachDestination(destination.into(), message)) +/// fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { +/// return Err(SendError::CannotReachDestination) /// } /// } /// /// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing. /// struct Sender2; /// impl SendXcm for Sender2 { -/// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// if let MultiLocation { parents: 0, interior: X2(j1, j2) } = destination.into() { +/// fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { +/// let d = destination.as_ref().ok_or(SendError::MissingArgument)?; +/// if let MultiLocation { parents: 0, interior: X2(j1, j2) } = d { /// Ok(()) /// } else { /// Err(SendError::Unroutable) @@ -354,11 +357,11 @@ pub type SendResult = result::Result<(), SendError>; /// /// A sender that accepts a message from a parent, passing through otherwise. /// struct Sender3; /// impl SendXcm for Sender3 { -/// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// let destination = destination.into(); -/// match destination { +/// fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { +/// let d = destination.as_ref().ok_or(SendError::MissingArgument)?; +/// match d { /// MultiLocation { parents: 1, interior: Here } => Ok(()), -/// _ => Err(SendError::CannotReachDestination(destination, message)), +/// _ => Err(SendError::CannotReachDestination), /// } /// } /// } @@ -372,38 +375,61 @@ pub type SendResult = result::Result<(), SendError>; /// call: call.into(), /// }]); /// -/// assert!( -/// // Sender2 will block this. -/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(Parent, message.clone()) -/// .is_err() -/// ); +/// // Sender2 will block this. +/// assert!(send_xcm::<(Sender1, Sender2, Sender3) as SendXcm>(Parent.into(), message.clone()).is_err()); /// -/// assert!( -/// // Sender3 will catch this. -/// <(Sender1, Sender3) as SendXcm>::send_xcm(Parent, message.clone()) -/// .is_ok() -/// ); +/// // Sender3 will catch this. +/// assert!(send_xcm::<(Sender1, Sender3) as SendXcm>(Parent.into(), message.clone()).is_ok()); /// # } /// ``` pub trait SendXcm { - /// Send an XCM `message` to a given `destination`. + /// Determine the cost which will be paid by this chain if the XCM `message` is sent to the + /// given `destination`. + /// + /// If it is not a destination which can be reached with this type but possibly could by others, + /// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple + /// implementation to exit early without trying other type fields. + fn send_cost(_: &MultiLocation, _: &Xcm<()>) -> SendCostResult { + Ok(MultiAssets::new()) + } + + /// Send an XCM `message` to a given `destination`, paying whatever must be paid for the message + /// to be delivered. /// - /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST* - /// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without - /// trying other type fields. - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult; + /// The `destination` and `message` must be `Some` (or else an error will be returned) and they + /// may only be consumed if the `Err` is not `CannotReachDestination`. + /// + /// If it is not a destination which can be reached with this type but possibly could by others, + /// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple + /// implementation to exit early without trying other type fields. + fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl SendXcm for Tuple { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { + fn send_cost(destination: &MultiLocation, message: &Xcm<()>) -> SendCostResult { for_tuples!( #( - // we shadow `destination` and `message` in each expansion for the next one. - let (destination, message) = match Tuple::send_xcm(destination, message) { - Err(SendError::CannotReachDestination(d, m)) => (d, m), + match Tuple::send_cost(destination, message) { + Err(SendError::CannotReachDestination) => {}, o @ _ => return o, }; )* ); - Err(SendError::CannotReachDestination(destination.into(), message)) + Err(SendError::CannotReachDestination) } + + fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + for_tuples!( #( + match Tuple::send_xcm(destination, message) { + Err(SendError::CannotReachDestination) => {}, + o @ _ => return o, + }; + )* ); + Err(SendError::CannotReachDestination) + } +} + +/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps +/// both in `Some` before passing them as as mutable references into `T::send_xcm`. +pub fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> SendResult { + T::send_xcm(&mut Some(dest), &mut Some(msg)) } diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index efea3a06e073..5dbcf8ae99af 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -41,7 +41,8 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), Ok(())); + let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); + assert_eq!(send_xcm::(dest, msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -68,8 +69,8 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get(), Parachain(1000)); - assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -94,8 +95,8 @@ fn sending_to_parachain_of_bridged_chain_works() { /// ``` #[test] fn sending_to_relay_chain_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get()); - assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Parent, Remote::get()).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs index 732cb0ddecae..193142d09214 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -37,7 +37,7 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(Router::send_xcm((Parent, Remote::get()), msg), Ok(())); + assert_eq!(send_xcm::((Parent, Remote::get()).into(), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -57,8 +57,8 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Remote::get(), Parachain(1000)); - assert_eq!(Router::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Remote::get(), Parachain(1000)).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index aa5760498312..ad968477efe5 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -21,6 +21,7 @@ use frame_support::{parameter_types, traits::Get}; use std::{cell::RefCell, marker::PhantomData}; use xcm::prelude::*; use xcm_executor::XcmExecutor; +use SendError::*; mod local_para_para; mod local_relay_relay; @@ -30,8 +31,8 @@ mod remote_para_para_via_relay; mod remote_relay_relay; parameter_types! { - pub Local: NetworkId = ByUri(b"local".to_vec()); - pub Remote: NetworkId = ByUri(b"remote".to_vec()); + pub Local: NetworkId = ByGenesis([0; 32]); + pub Remote: NetworkId = ByGenesis([1; 32]); } std::thread_local! { @@ -42,7 +43,7 @@ struct TestBridge(PhantomData); impl TestBridge { fn service() -> u64 { BRIDGE_TRAFFIC - .with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).unwrap_or(0)).sum()) + .with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).map_or(0, |()| 1)).sum()) } } impl HaulBlob for TestBridge { @@ -56,9 +57,9 @@ std::thread_local! { } struct TestRemoteIncomingRouter; impl SendXcm for TestRemoteIncomingRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push((destination, message))); + fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + let pair = (destination.take().unwrap(), message.take().unwrap()); + REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push(pair)); Ok(()) } } @@ -75,26 +76,27 @@ struct UnpaidExecutingRouter( impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if destination == Remote::get().relative_to(&Local::get()) { - // We now pretend that the message was delivered from `Local` to `Remote`, and execute - // so we need to ensure that the `TestConfig` is set up properly for executing as - // though it is `Remote`. - ExecutorUniversalLocation::set(Remote::get()); - let origin = Local::get().relative_to(&Remote::get()); - AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RemoteExporter::export_xcm); - // The we execute it: - let outcome = - XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); - return match outcome { - Outcome::Complete(..) => Ok(()), - Outcome::Incomplete(..) => Err(SendError::Transport("Error executing")), - Outcome::Error(..) => Err(SendError::Transport("Unable to execute")), - } + fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + let expect_dest = Remote::get().relative_to(&Local::get()); + if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { + return Err(CannotReachDestination) + } + let message = message.take().ok_or(MissingArgument)?; + // We now pretend that the message was delivered from `Local` to `Remote`, and execute + // so we need to ensure that the `TestConfig` is set up properly for executing as + // though it is `Remote`. + ExecutorUniversalLocation::set(Remote::get()); + let origin = Local::get().relative_to(&Remote::get()); + AllowUnpaidFrom::set(vec![origin.clone()]); + set_exporter_override(RemoteExporter::export_xcm); + // The we execute it: + let outcome = + XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + match outcome { + Outcome::Complete(..) => Ok(()), + Outcome::Incomplete(..) => Err(Transport("Error executing")), + Outcome::Error(..) => Err(Transport("Unable to execute")), } - Err(SendError::CannotReachDestination(destination, message)) } } @@ -105,25 +107,26 @@ struct ExecutingRouter(PhantomData<(Local, Remote impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - let destination = destination.into(); - if destination == Remote::get().relative_to(&Local::get()) { - // We now pretend that the message was delivered from `Local` to `Remote`, and execute - // so we need to ensure that the `TestConfig` is set up properly for executing as - // though it is `Remote`. - ExecutorUniversalLocation::set(Remote::get()); - let origin = Local::get().relative_to(&Remote::get()); - AllowPaidFrom::set(vec![origin.clone()]); - set_exporter_override(RemoteExporter::export_xcm); - // The we execute it: - let outcome = - XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); - return match outcome { - Outcome::Complete(..) => Ok(()), - Outcome::Incomplete(..) => Err(SendError::Transport("Error executing")), - Outcome::Error(..) => Err(SendError::Transport("Unable to execute")), - } + fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + let expect_dest = Remote::get().relative_to(&Local::get()); + if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { + return Err(CannotReachDestination) + } + let message = message.take().ok_or(MissingArgument)?; + // We now pretend that the message was delivered from `Local` to `Remote`, and execute + // so we need to ensure that the `TestConfig` is set up properly for executing as + // though it is `Remote`. + ExecutorUniversalLocation::set(Remote::get()); + let origin = Local::get().relative_to(&Remote::get()); + AllowPaidFrom::set(vec![origin.clone()]); + set_exporter_override(RemoteExporter::export_xcm); + // The we execute it: + let outcome = + XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + return match outcome { + Outcome::Complete(..) => Ok(()), + Outcome::Incomplete(..) => Err(Transport("Error executing")), + Outcome::Error(..) => Err(Transport("Unable to execute")), } - Err(SendError::CannotReachDestination(destination, message)) } } diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs index 757c8ca09705..9be8f3c96bac 100644 --- a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -53,10 +53,10 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); /// ``` #[test] fn sending_to_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get()); + let dest: MultiLocation = (Parent, Parent, Remote::get()).into(); // Routing won't work if we don't have enough funds. assert_eq!( - LocalRouter::send_xcm(dest.clone(), Xcm(vec![Trap(1)])), + send_xcm::(dest.clone(), Xcm(vec![Trap(1)])), Err(SendError::Transport("Error executing")), ); @@ -64,7 +64,7 @@ fn sending_to_bridged_chain_works() { add_asset(to_account(Parachain(100)).unwrap(), (Here, 100)); let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm(dest, msg), Ok(())); + assert_eq!(send_xcm::(dest, msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -94,17 +94,17 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get(), Parachain(100)); + let dest: MultiLocation = (Parent, Parent, Remote::get(), Parachain(100)).into(); // Routing won't work if we don't have enough funds. assert_eq!( - LocalRouter::send_xcm(dest.clone(), Xcm(vec![Trap(1)])), + send_xcm::(dest.clone(), Xcm(vec![Trap(1)])), Err(SendError::Transport("Error executing")), ); // Initialize the local relay so that our parachain has funds to pay for export. add_asset(to_account(Parachain(100)).unwrap(), (Here, 1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(100).into(), diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index 60fdba6a5397..f580673e0b81 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -50,7 +50,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!( - ::send_xcm((Parent, Parent, Remote::get(), Parachain(1)), msg), + send_xcm::((Parent, Parent, Remote::get(), Parachain(1)).into(), msg), Ok(()) ); assert_eq!(TheBridge::service(), 1); @@ -79,8 +79,8 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_sibling_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get(), Parachain(1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -105,8 +105,8 @@ fn sending_to_sibling_of_bridged_chain_works() { /// ``` #[test] fn sending_to_relay_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get()); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Parent, Remote::get()).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index dfe4ef047d84..b9e4fe32099f 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -50,7 +50,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!( - ::send_xcm((Parent, Remote::get(), Parachain(1)), msg), + send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), Ok(()) ); assert_eq!(TheBridge::service(), 1); @@ -72,8 +72,8 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_sibling_of_bridged_chain_works() { - let dest = (Parent, Remote::get(), Parachain(1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Remote::get(), Parachain(1000)).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -94,8 +94,8 @@ fn sending_to_sibling_of_bridged_chain_works() { /// ``` #[test] fn sending_to_relay_of_bridged_chain_works() { - let dest = (Parent, Remote::get()); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Remote::get()).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parent.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; assert_eq!(take_received_remote_messages(), expected); diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index fa7bdf2c5acb..39885b0edcd0 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -49,7 +49,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(::send_xcm((Parent, Parent, Remote::get()), msg), Ok(())); + assert_eq!(send_xcm::((Parent, Parent, Remote::get()).into(), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -64,7 +64,7 @@ fn sending_to_bridged_chain_works() { ); } -/// ``` nocompile +/// ```nocompile /// local | remote /// | /// GlobalConsensus(Local::get()) ========> GlobalConsensus(Remote::get()) @@ -76,8 +76,8 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { - let dest = (Parent, Parent, Remote::get(), Parachain(1000)); - assert_eq!(LocalRouter::send_xcm(dest, Xcm(vec![Trap(1)])), Ok(())); + let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(1000).into(), diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 740e184f9a4d..d1df9e087021 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -103,10 +103,10 @@ impl GetDispatchInfo for TestCall { } thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); - pub static EXPORTED_XCM: RefCell> = RefCell::new(Vec::new()); + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); + pub static EXPORTED_XCM: RefCell)>> = RefCell::new(Vec::new()); pub static EXPORTER_OVERRIDE: RefCell) -> SendResult + fn(NetworkId, u32, &mut Option, &mut Option>) -> SendResult >> = RefCell::new(None); } pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { @@ -115,7 +115,7 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm)> { EXPORTED_XCM.with(|q| (*q.borrow()).clone()) } -pub fn set_exporter_override(f: fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> SendResult) { +pub fn set_exporter_override(f: fn(NetworkId, u32, &mut Option, &mut Option>) -> SendResult) { EXPORTER_OVERRIDE.with(|x| x.replace(Some(f))); } #[allow(dead_code)] @@ -124,8 +124,9 @@ pub fn clear_exporter_override() { } pub struct TestMessageSender; impl SendXcm for TestMessageSender { - fn send_xcm(dest: impl Into, msg: opaque::Xcm) -> SendResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest.into(), msg))); + fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + let pair = (dest.take().unwrap(), msg.take().unwrap()); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } } @@ -134,14 +135,15 @@ impl ExportXcm for TestMessageExporter { fn export_xcm( network: NetworkId, channel: u32, - dest: impl Into, - msg: opaque::Xcm, + dest: &mut Option, + msg: &mut Option>, ) -> SendResult { EXPORTER_OVERRIDE.with(|e| { if let Some(ref f) = &*e.borrow() { - f(network, channel, dest.into(), msg) + f(network, channel, dest, msg) } else { - EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest.into(), msg))); + let (dest, msg) = (dest.take().unwrap(), msg.take().unwrap()); + EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest, msg))); Ok(()) } }) @@ -316,7 +318,7 @@ pub fn response(query_id: u64) -> Option { parameter_types! { pub static ExecutorUniversalLocation: InteriorMultiLocation - = (ByUri(b"local"[..].into()), Parachain(42)).into(); + = (ByGenesis([0; 32]), Parachain(42)).into(); pub UnitWeightCost: Weight = 10; } parameter_types! { diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index c95523a20eee..07638c80fb06 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -21,6 +21,7 @@ use parity_scale_codec::{Decode, Encode}; use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::ExportXcm; +use SendError::*; fn ensure_is_remote( universal_local: impl Into, @@ -58,29 +59,36 @@ pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancest impl> SendXcm for LocalUnpaidExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let devolved = match ensure_is_remote(Ancestry::get(), dest) { + // TODO: include price for use of `Exporter` in `send_cost` impl. + + fn send_xcm(dest: &mut Option, xcm: &mut Option>) -> SendResult { + let d = dest.take().ok_or(MissingArgument)?; + let devolved = match ensure_is_remote(Ancestry::get(), d) { Ok(x) => x, - Err(dest) => return Err(SendError::CannotReachDestination(dest, xcm)), + Err(d) => { + *dest = Some(d); + return Err(CannotReachDestination); + }, }; let (network, destination, local_network, local_location) = devolved; + let inner = xcm.take().ok_or(MissingArgument)?; let mut message: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); if local_location != Here { message.inner_mut().push(DescendOrigin(local_location)); } - message.inner_mut().extend(xcm.into_iter()); - Exporter::export_xcm(network, 0, destination, message) + message.inner_mut().extend(inner.into_iter()); + Exporter::export_xcm(network, 0, &mut Some(destination), &mut Some(message)) } } -// TODO: `SendXcm` should include a price/weight calculator for calling prior to `send_xcm`. - pub trait ExporterFor { /// Return the locally-routable bridge (if any) capable of forwarding `message` to the /// `remote_location` on the remote `network`, together with the payment which is required. /// - /// The payment is specified from the context of the bridge, not the local chain. + /// The payment is specified from the context of the bridge, not the local chain. This is the# + /// total amount to withdraw in to Holding and should cover both payment for the execution on + /// the bridge chain as well as payment for the use of the `ExportMessage` instruction. fn exporter_for( network: &NetworkId, remote_location: &InteriorMultiLocation, @@ -135,30 +143,27 @@ pub struct UnpaidRemoteExporter( impl> SendXcm for UnpaidRemoteExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let dest = dest.into(); + // TODO: include `Router::send_cost` for getting the `ExportMessage` to the bridge. - // TODO: proper matching so we can be sure that it's the only viable send_xcm before we - // attempt and thus can acceptably consume dest & xcm. - let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); - - let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; + fn send_xcm(dest: &mut Option, xcm: &mut Option>) -> SendResult { + let d = dest.as_ref().ok_or(MissingArgument)?.clone(); + let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| CannotReachDestination)?; let (remote_network, remote_location, local_network, local_location) = devolved; // Prepend the desired message with instructions which effectively rewrite the origin. // // This only works because the remote chain empowers the bridge // to speak for the local network. - let mut inner_xcm: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); + let mut exported: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); if local_location != Here { - inner_xcm.inner_mut().push(DescendOrigin(local_location)); + exported.inner_mut().push(DescendOrigin(local_location)); } - inner_xcm.inner_mut().extend(xcm.into_iter()); + exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); let (bridge, payment) = - Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) - .ok_or(err.clone())?; - ensure!(payment.is_none(), err); + Bridges::exporter_for(&remote_network, &remote_location, &exported) + .ok_or(CannotReachDestination)?; + ensure!(payment.is_none(), Unroutable); // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message @@ -166,9 +171,9 @@ impl let message = Xcm(vec![ExportMessage { network: remote_network, destination: remote_location, - xcm: inner_xcm, + xcm: exported, }]); - Router::send_xcm(bridge, message) + send_xcm::(bridge, message) } } @@ -186,38 +191,40 @@ pub struct SovereignPaidRemoteExporter( impl> SendXcm for SovereignPaidRemoteExporter { - fn send_xcm(dest: impl Into, xcm: Xcm<()>) -> SendResult { - let dest = dest.into(); - - // TODO: proper matching so we can be sure that it's the only viable send_xcm before we - // attempt and thus can acceptably consume dest & xcm. - let err = SendError::CannotReachDestination(dest.clone(), xcm.clone()); + // TODO: include `Router::send_cost` for getting the `ExportMessage` to the bridge and the + // total cost paid at the bridge `Exporter::export_price` in the `send_cost` impl. - let devolved = ensure_is_remote(Ancestry::get(), dest).map_err(|_| err.clone())?; + fn send_xcm(dest: &mut Option, xcm: &mut Option>) -> SendResult { + let d = dest.as_ref().ok_or(MissingArgument)?.clone(); + let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| CannotReachDestination)?; let (remote_network, remote_location, local_network, local_location) = devolved; // Prepend the desired message with instructions which effectively rewrite the origin. // // This only works because the remote chain empowers the bridge // to speak for the local network. - let mut inner_xcm: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); + let mut exported: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_network))].into(); if local_location != Here { - inner_xcm.inner_mut().push(DescendOrigin(local_location)); + exported.inner_mut().push(DescendOrigin(local_location)); } - inner_xcm.inner_mut().extend(xcm.into_iter()); + exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); let (bridge, maybe_payment) = - Bridges::exporter_for(&remote_network, &remote_location, &inner_xcm) - .ok_or(err.clone())?; + Bridges::exporter_for(&remote_network, &remote_location, &exported) + .ok_or(CannotReachDestination)?; + let local_from_bridge = MultiLocation::from(Ancestry::get()) .inverted(&bridge) - .map_err(|_| err.clone())?; + .map_err(|_| Unroutable)?; // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message // export for free. Common-good chains will typically be afforded this. - let export_instruction = - ExportMessage { network: remote_network, destination: remote_location, xcm: inner_xcm }; + let export_instruction = ExportMessage { + network: remote_network, + destination: remote_location, + xcm: exported, + }; let message = Xcm(if let Some(payment) = maybe_payment { vec![ @@ -230,13 +237,17 @@ impl } else { vec![export_instruction] }); - Router::send_xcm(bridge, message) + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. This will only work if the bridge will do the message + // export for free. Common-good chains will typically be afforded this. + send_xcm::(bridge, message) } } pub trait DispatchBlob { /// Dispatches an incoming blob and returns the unexpectable weight consumed by the dispatch. - fn dispatch_blob(blob: Vec) -> Result; + fn dispatch_blob(blob: Vec) -> Result<(), DispatchBlobError>; } pub trait HaulBlob { @@ -267,7 +278,7 @@ pub struct BridgeBlobDispatcher(PhantomData<(Router, OurPlace) impl> DispatchBlob for BridgeBlobDispatcher { - fn dispatch_blob(blob: Vec) -> Result { + fn dispatch_blob(blob: Vec) -> Result<(), DispatchBlobError> { let our_universal = OurPlace::get(); let our_global = our_universal.global_consensus().map_err(|()| DispatchBlobError::Unbridgable)?; @@ -285,9 +296,8 @@ impl> DispatchBlob let dest = universal_dest.relative_to(&our_universal); let message: Xcm<()> = message.try_into().map_err(|_| DispatchBlobError::UnsupportedXcmVersion)?; - Router::send_xcm(dest, message).map_err(|_| DispatchBlobError::RoutingError)?; - // TODO: Proper weight. - Ok(1) + send_xcm::(dest, message).map_err(|_| DispatchBlobError::RoutingError)?; + Ok(()) } } @@ -295,25 +305,26 @@ pub struct HaulBlobExporter(PhantomData<(Bridge, Bridged impl> ExportXcm for HaulBlobExporter { + // TODO: Get impl for what value to return for `export_price`, then tests for it. + fn export_xcm( network: NetworkId, _channel: u32, - destination: impl Into, - message: Xcm<()>, + destination: &mut Option, + message: &mut Option>, ) -> Result<(), SendError> { - let destination = destination.into(); let bridged_network = BridgedNetwork::get(); - ensure!( - &network == &bridged_network, - SendError::CannotReachNetwork(network, destination, message) - ); + ensure!(&network == &bridged_network, SendError::CannotReachDestination); // We don't/can't use the `channel` for this adapter. - let universal_dest = match destination.pushed_front_with(GlobalConsensus(bridged_network)) { + let dest = destination.take().ok_or(SendError::MissingArgument)?; + let universal_dest = match dest.pushed_front_with(GlobalConsensus(bridged_network)) { Ok(d) => d.into(), - Err((destination, _)) => - return Err(SendError::CannotReachNetwork(network, destination, message)), + Err((dest, _)) => { + *destination = Some(dest); + return Err(SendError::CannotReachDestination); + }, }; - let message = VersionedXcm::from(message); + let message = VersionedXcm::from(message.take().ok_or(SendError::MissingArgument)?); Bridge::haul_blob(BridgeMessage { universal_dest, message }.encode()); Ok(()) } diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index ee3303df4c7c..e541ee0e44c6 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -47,8 +47,9 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { } pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: impl Into, msg: opaque::Xcm) -> SendResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest.into(), msg))); + fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + let pair = (dest.take().unwrap(), msg.take().unwrap()); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 8c678b51657c..b287f28c9932 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -32,7 +32,7 @@ pub mod traits; use traits::{ ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, - WeightTrader, + WeightTrader, export_xcm, }; mod assets; @@ -319,7 +319,7 @@ impl XcmExecutor { assets.reanchor(&dest, &context).map_err(|()| XcmError::MultiLocationFull)?; let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) + send_xcm::(dest, Xcm(message)).map_err(Into::into) }, ReceiveTeleportedAsset(assets) => { let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); @@ -422,7 +422,7 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest, None); let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) + send_xcm::(dest, Xcm(message)).map_err(Into::into) }, InitiateReserveWithdraw { assets, reserve, xcm } => { // Note that here we are able to place any assets which could not be reanchored @@ -434,7 +434,7 @@ impl XcmExecutor { ); let mut message = vec![WithdrawAsset(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - Config::XcmSender::send_xcm(reserve, Xcm(message)).map_err(Into::into) + send_xcm::(reserve, Xcm(message)).map_err(Into::into) }, InitiateTeleport { assets, dest, xcm } => { // We must do this first in order to resolve wildcards. @@ -447,7 +447,7 @@ impl XcmExecutor { let assets = Self::reanchored(assets, &dest, None); let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into) + send_xcm::(dest, Xcm(message)).map_err(Into::into) }, ReportHolding { response_info, assets } => { // Note that we pass `None` as `maybe_failed_bin` since no assets were ever removed @@ -545,7 +545,7 @@ impl XcmExecutor { let querier = Self::to_querier(self.origin.clone(), &destination)?; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - Config::XcmSender::send_xcm(destination, message).map_err(Into::into) + send_xcm::(destination, message).map_err(Into::into) }, ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => { let pallet = Config::PalletInstancesInfo::infos() @@ -585,9 +585,12 @@ impl XcmExecutor { // Hash identifies the lane on the exporter which we use. We use the pairwise // combination of the origin and destination to ensure origin/destination pairs will // generally have their own lanes. + let fee = Config::MessageExporter::export_price(network, &destination, &xcm)?; + self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; + let hash = (&self.origin, &destination).using_encoded(blake2_128); let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); - Config::MessageExporter::export_xcm(network, channel, destination, xcm)?; + export_xcm::(network, channel, destination, xcm)?; Ok(()) }, ExchangeAsset { .. } => Err(XcmError::Unimplemented), @@ -623,7 +626,7 @@ impl XcmExecutor { let QueryResponseInfo { destination, query_id, max_weight } = info; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - Config::XcmSender::send_xcm(destination, message).map_err(Into::into) + send_xcm::(destination, message).map_err(Into::into) } /// NOTE: Any assets which were unable to be reanchored are introduced into `failed_bin`. diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index 2adb46d9d93e..f0296090e54b 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -18,30 +18,61 @@ use xcm::latest::prelude::*; /// Type which is able to send a given message over to another consensus network. pub trait ExportXcm { + fn export_price( + _network: NetworkId, + _destination: &InteriorMultiLocation, + _message: &Xcm<()>, + ) -> SendCostResult { + Ok(MultiAssets::new()) + } + fn export_xcm( network: NetworkId, channel: u32, - destination: impl Into, - message: Xcm<()>, - ) -> Result<(), SendError>; + destination: &mut Option, + message: &mut Option>, + ) -> SendResult; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl ExportXcm for Tuple { + fn export_price( + network: NetworkId, + dest: &InteriorMultiLocation, + message: &Xcm<()>, + ) -> SendCostResult { + for_tuples!( #( + match Tuple::export_price(network, dest, message) { + Err(SendError::CannotReachDestination) => {}, + o @ _ => return o, + }; + )* ); + Err(SendError::CannotReachDestination) + } + fn export_xcm( network: NetworkId, channel: u32, - dest: impl Into, - message: Xcm<()>, + dest: &mut Option, + message: &mut Option>, ) -> SendResult { - let dest = dest.into(); for_tuples!( #( - // we shadow `dest` and `message` in each expansion for the next one. - let (network, dest, message) = match Tuple::export_xcm(network, channel, dest, message) { - Err(SendError::CannotReachNetwork(n, d, m)) => (n, d, m), + match Tuple::export_xcm(network, channel, dest, message) { + Err(SendError::CannotReachDestination) => {}, o @ _ => return o, }; )* ); - Err(SendError::CannotReachNetwork(network, dest, message)) + Err(SendError::CannotReachDestination) } } + +/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps +/// both in `Some` before passing them as as mutable references into `T::send_xcm`. +pub fn export_xcm( + network: NetworkId, + channel: u32, + dest: InteriorMultiLocation, + message: Xcm<()>, +) -> SendResult { + T::export_xcm(network, channel, &mut Some(dest), &mut Some(message)) +} diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index c1e75447d6be..57cac96387de 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -23,7 +23,7 @@ pub use conversion::{ mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; -pub use export::ExportXcm; +pub use export::{ExportXcm, export_xcm}; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 4bdc1cbd17e6..3bbf956c0242 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -95,7 +95,7 @@ impl configuration::Config for Runtime { parameter_types! { pub const TokenLocation: MultiLocation = Here.into_location(); - pub RelayNetwork: NetworkId = ByUri((&b"example.com"[..]).to_owned()); + pub RelayNetwork: NetworkId = ByGenesis([0; 32]); pub const AnyNetwork: Option = None; pub Ancestry: InteriorMultiLocation = Here; pub UnitWeightCost: Weight = 1_000; diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index d86d8bf1445b..048d858e8304 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -95,7 +95,7 @@ impl configuration::Config for Runtime { parameter_types! { pub const TokenLocation: MultiLocation = Here.into_location(); - pub const ThisNetwork: NetworkId = NetworkId::ByUri(Vec::new()); + pub const ThisNetwork: NetworkId = NetworkId::ByGenesis([0; 32]); pub const AnyNetwork: Option = None; pub const Ancestry: InteriorMultiLocation = Here; pub const UnitWeightCost: Weight = 1_000; diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 5e563e153dba..c4aa3adc3a29 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -296,45 +296,45 @@ macro_rules! decl_test_network { pub struct ParachainXcmRouter($crate::PhantomData); impl> $crate::SendXcm for ParachainXcmRouter { - fn send_xcm(destination: impl Into<$crate::MultiLocation>, message: $crate::Xcm<()>) -> $crate::SendResult { + fn send_xcm(destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>) -> $crate::SendResult { use $crate::{UmpSink, XcmpMessageHandlerT}; - let destination = destination.into(); - match destination.interior() { - $crate::Junctions::Here if destination.parent_count() == 1 => { - $crate::PARA_MESSAGE_BUS.with( - |b| b.borrow_mut().push_back((T::get(), destination, message))); - Ok(()) - }, + let d = destination.take().ok_or($crate::SendError::MissingArgument)?; + match (d.interior(), d.parent_count()) { + ($crate::Junctions::Here, 1) => {}, $( - $crate::X1($crate::Parachain(id)) if *id == $para_id && destination.parent_count() == 1 => { - $crate::PARA_MESSAGE_BUS.with( - |b| b.borrow_mut().push_back((T::get(), destination, message))); - Ok(()) - }, + ($crate::X1($crate::Parachain(id)), 1) if id == &$para_id => {} )* - _ => Err($crate::SendError::CannotReachDestination(destination, message)), + _ => { + *destination = Some(d); + return Err($crate::SendError::CannotReachDestination) + }, } + let m = message.take().ok_or($crate::SendError::MissingArgument)?; + $crate::PARA_MESSAGE_BUS.with(|b| b.borrow_mut().push_back((T::get(), d, m))); + Ok(()) } } /// XCM router for relay chain. pub struct RelayChainXcmRouter; impl $crate::SendXcm for RelayChainXcmRouter { - fn send_xcm(destination: impl Into<$crate::MultiLocation>, message: $crate::Xcm<()>) -> $crate::SendResult { + fn send_xcm(destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>) -> $crate::SendResult { use $crate::DmpMessageHandlerT; - let destination = destination.into(); - match destination.interior() { + let d = destination.take().ok_or($crate::SendError::MissingArgument)?; + match (d.interior(), d.parent_count()) { $( - $crate::X1($crate::Parachain(id)) if *id == $para_id && destination.parent_count() == 0 => { - $crate::RELAY_MESSAGE_BUS.with( - |b| b.borrow_mut().push_back((destination, message))); - Ok(()) - }, + ($crate::X1($crate::Parachain(id)), 0) if id == &$para_id => {}, )* - _ => Err($crate::SendError::Unroutable), + _ => { + *destination = Some(d); + return Err($crate::SendError::CannotReachDestination) + }, } + let m = message.take().ok_or($crate::SendError::MissingArgument)?; + $crate::RELAY_MESSAGE_BUS.with(|b| b.borrow_mut().push_back((d, m))); + Ok(()) } } }; From 65374019f60973f7868079b86674e749a8b65f1b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 4 Feb 2022 19:05:16 +0000 Subject: [PATCH 39/57] Fixes --- xcm/src/v3/traits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 9f9432e4deec..d935d1e91a6b 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -376,10 +376,10 @@ pub type SendCostResult = result::Result; /// }]); /// /// // Sender2 will block this. -/// assert!(send_xcm::<(Sender1, Sender2, Sender3) as SendXcm>(Parent.into(), message.clone()).is_err()); +/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err()); /// /// // Sender3 will catch this. -/// assert!(send_xcm::<(Sender1, Sender3) as SendXcm>(Parent.into(), message.clone()).is_ok()); +/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok()); /// # } /// ``` pub trait SendXcm { From f2f844eef5de9bd422540b27c57e8e6940a0dcfa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 4 Feb 2022 19:07:42 +0000 Subject: [PATCH 40/57] Formatting --- runtime/common/src/xcm_sender.rs | 10 +++------ xcm/src/v3/mod.rs | 10 ++++----- xcm/src/v3/traits.rs | 17 +++++++++------ .../src/bridging_tests/local_para_para.rs | 2 +- xcm/xcm-builder/src/bridging_tests/mod.rs | 15 ++++++++++--- .../remote_para_para_via_relay.rs | 5 +---- xcm/xcm-builder/src/mock.rs | 4 +++- xcm/xcm-builder/src/universal_exports.rs | 21 +++++++------------ xcm/xcm-executor/src/lib.rs | 4 ++-- xcm/xcm-executor/src/traits/mod.rs | 2 +- 10 files changed, 47 insertions(+), 43 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 9c59890b8e11..dd8bb4bb2296 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -34,19 +34,15 @@ impl SendXcm *id } else { *dest = Some(d); - return Err(CannotReachDestination); + return Err(CannotReachDestination) }; // Downward message passing. let xcm = msg.take().ok_or(MissingArgument)?; let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?; let config = >::config(); - >::queue_downward_message( - &config, - id.into(), - versioned_xcm.encode(), - ) - .map_err(Into::::into)?; + >::queue_downward_message(&config, id.into(), versioned_xcm.encode()) + .map_err(Into::::into)?; Ok(()) } } diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index b43e704d8a81..c7da7347ee7b 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -44,8 +44,8 @@ pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; pub use traits::{ - Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, SendResult, SendCostResult, - SendXcm, Weight, send_xcm, + send_xcm, Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendCostResult, SendError, + SendResult, SendXcm, Weight, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -165,7 +165,7 @@ impl From> for Vec> { pub mod prelude { mod contents { pub use super::super::{ - Ancestor, AncestorThen, + send_xcm, Ancestor, AncestorThen, AssetId::{self, *}, AssetInstance::{self, *}, BodyId, BodyPart, Error as XcmError, ExecuteXcm, @@ -179,8 +179,8 @@ pub mod prelude { MultiAssets, MultiLocation, NetworkId::{self, *}, OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, - QueryResponseInfo, Response, Result as XcmResult, SendError, SendCostResult, SendResult, - SendXcm, send_xcm, + QueryResponseInfo, Response, Result as XcmResult, SendCostResult, SendError, + SendResult, SendXcm, WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index d935d1e91a6b..9f69baf269ca 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -167,10 +167,9 @@ impl TryFrom for Error { impl From for Error { fn from(e: SendError) -> Self { match e { - SendError::CannotReachDestination - | SendError::Unroutable - | SendError::MissingArgument - => Error::Unroutable, + SendError::CannotReachDestination | + SendError::Unroutable | + SendError::MissingArgument => Error::Unroutable, SendError::Transport(s) => Error::Transport(s), SendError::DestinationUnsupported => Error::DestinationUnsupported, SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, @@ -402,7 +401,10 @@ pub trait SendXcm { /// If it is not a destination which can be reached with this type but possibly could by others, /// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple /// implementation to exit early without trying other type fields. - fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult; + fn send_xcm( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult; } #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -417,7 +419,10 @@ impl SendXcm for Tuple { Err(SendError::CannotReachDestination) } - fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + fn send_xcm( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { for_tuples!( #( match Tuple::send_xcm(destination, message) { Err(SendError::CannotReachDestination) => {}, diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index 5dbcf8ae99af..7afd2009a48b 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -41,7 +41,7 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); + let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); assert_eq!(send_xcm::(dest, msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index ad968477efe5..d5aa817f3f2c 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -57,7 +57,10 @@ std::thread_local! { } struct TestRemoteIncomingRouter; impl SendXcm for TestRemoteIncomingRouter { - fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + fn send_xcm( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { let pair = (destination.take().unwrap(), message.take().unwrap()); REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push(pair)); Ok(()) @@ -76,7 +79,10 @@ struct UnpaidExecutingRouter( impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { - fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + fn send_xcm( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { return Err(CannotReachDestination) @@ -107,7 +113,10 @@ struct ExecutingRouter(PhantomData<(Local, Remote impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { - fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { + fn send_xcm( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { return Err(CannotReachDestination) diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index b9e4fe32099f..174aaec0089a 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -49,10 +49,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!( - send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), - Ok(()) - ); + assert_eq!(send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), Ok(())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index d1df9e087021..76d1e8a759c9 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -115,7 +115,9 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm)> { EXPORTED_XCM.with(|q| (*q.borrow()).clone()) } -pub fn set_exporter_override(f: fn(NetworkId, u32, &mut Option, &mut Option>) -> SendResult) { +pub fn set_exporter_override( + f: fn(NetworkId, u32, &mut Option, &mut Option>) -> SendResult, +) { EXPORTER_OVERRIDE.with(|x| x.replace(Some(f))); } #[allow(dead_code)] diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 07638c80fb06..ff82d42e6eb5 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -67,7 +67,7 @@ impl> SendXcm Ok(x) => x, Err(d) => { *dest = Some(d); - return Err(CannotReachDestination); + return Err(CannotReachDestination) }, }; let (network, destination, local_network, local_location) = devolved; @@ -160,9 +160,8 @@ impl } exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); - let (bridge, payment) = - Bridges::exporter_for(&remote_network, &remote_location, &exported) - .ok_or(CannotReachDestination)?; + let (bridge, payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) + .ok_or(CannotReachDestination)?; ensure!(payment.is_none(), Unroutable); // We then send a normal message to the bridge asking it to export the prepended @@ -213,18 +212,14 @@ impl Bridges::exporter_for(&remote_network, &remote_location, &exported) .ok_or(CannotReachDestination)?; - let local_from_bridge = MultiLocation::from(Ancestry::get()) - .inverted(&bridge) - .map_err(|_| Unroutable)?; + let local_from_bridge = + MultiLocation::from(Ancestry::get()).inverted(&bridge).map_err(|_| Unroutable)?; // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message // export for free. Common-good chains will typically be afforded this. - let export_instruction = ExportMessage { - network: remote_network, - destination: remote_location, - xcm: exported, - }; + let export_instruction = + ExportMessage { network: remote_network, destination: remote_location, xcm: exported }; let message = Xcm(if let Some(payment) = maybe_payment { vec![ @@ -321,7 +316,7 @@ impl> ExportXcm Ok(d) => d.into(), Err((dest, _)) => { *destination = Some(dest); - return Err(SendError::CannotReachDestination); + return Err(SendError::CannotReachDestination) }, }; let message = VersionedXcm::from(message.take().ok_or(SendError::MissingArgument)?); diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index b287f28c9932..7c238099ef2f 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -30,9 +30,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, + export_xcm, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, - WeightTrader, export_xcm, + WeightTrader, }; mod assets; diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index 57cac96387de..a51854612bf0 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -23,7 +23,7 @@ pub use conversion::{ mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; -pub use export::{ExportXcm, export_xcm}; +pub use export::{export_xcm, ExportXcm}; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; From cfed3331cb8f58978e08a2821d62495f0f66dbc3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 5 Feb 2022 16:20:27 +0000 Subject: [PATCH 41/57] Refactor SendXcm to become two-phase --- runtime/common/src/xcm_sender.rs | 21 +++- runtime/parachains/src/dmp.rs | 15 +++ runtime/test-runtime/src/xcm_config.rs | 3 +- xcm/pallet-xcm-benchmarks/src/mock.rs | 6 +- xcm/pallet-xcm/src/lib.rs | 11 +- xcm/pallet-xcm/src/mock.rs | 22 +++- xcm/src/v3/mod.rs | 10 +- xcm/src/v3/traits.rs | 105 ++++++++++++------ .../src/bridging_tests/local_para_para.rs | 6 +- .../src/bridging_tests/local_relay_relay.rs | 4 +- xcm/xcm-builder/src/bridging_tests/mod.rs | 38 +++++-- .../bridging_tests/paid_remote_relay_relay.rs | 4 +- .../src/bridging_tests/remote_para_para.rs | 6 +- .../remote_para_para_via_relay.rs | 6 +- .../src/bridging_tests/remote_relay_relay.rs | 4 +- xcm/xcm-builder/src/mock.rs | 15 ++- xcm/xcm-builder/src/universal_exports.rs | 67 +++++++---- xcm/xcm-builder/tests/mock/mod.rs | 9 +- xcm/xcm-executor/src/lib.rs | 24 +++- xcm/xcm-executor/src/traits/export.rs | 10 +- xcm/xcm-simulator/src/lib.rs | 26 ++++- 21 files changed, 288 insertions(+), 124 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index dd8bb4bb2296..e7a6d8bcec10 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -17,9 +17,10 @@ //! XCM sender for relay chain. use parity_scale_codec::Encode; -use runtime_parachains::{configuration, dmp}; +use primitives::v1::Id as ParaId; +use runtime_parachains::{configuration::{self, HostConfiguration}, dmp}; use sp_std::marker::PhantomData; -use xcm::latest::prelude::*; +use xcm::prelude::*; use SendError::*; /// XCM sender for relay chain. It only sends downward message. @@ -28,7 +29,9 @@ pub struct ChildParachainRouter(PhantomData<(T, W)>); impl SendXcm for ChildParachainRouter { - fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + type OptionTicket = Option<(HostConfiguration, ParaId, Vec)>; + + fn validate(dest: &mut Option, msg: &mut Option>) -> SendResult { let d = dest.take().ok_or(MissingArgument)?; let id = if let MultiLocation { parents: 0, interior: X1(Parachain(id)) } = &d { *id @@ -39,10 +42,18 @@ impl SendXcm // Downward message passing. let xcm = msg.take().ok_or(MissingArgument)?; - let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?; let config = >::config(); - >::queue_downward_message(&config, id.into(), versioned_xcm.encode()) + let para = id.into(); + let blob = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?.encode(); + >::can_queue_downward_message(&config, ¶, &blob) .map_err(Into::::into)?; + + Ok(((config, para, blob), MultiAssets::new())) + } + + fn deliver((config, para, blob): (HostConfiguration, ParaId, VersionedXcm<()>)) -> Result<(), SendError> { + let r = >::queue_downward_message(&config, para, blob); + debug_assert!(r.is_ok(), "Unexpected error in DMP delivery {:?}", r); Ok(()) } } diff --git a/runtime/parachains/src/dmp.rs b/runtime/parachains/src/dmp.rs index c55a07b9f9ec..a00d194dbf02 100644 --- a/runtime/parachains/src/dmp.rs +++ b/runtime/parachains/src/dmp.rs @@ -134,6 +134,21 @@ impl Pallet { ::DownwardMessageQueueHeads::remove(outgoing_para); } + /// Determine whether enqueuing a downward message to a specific recipient para would result + /// in an error. If this returns `Ok(())` the caller can be certain that a call to + /// `queue_downward_message` with the same parameters will be successful. + pub fn can_queue_downward_message( + config: &HostConfiguration, + _para: &ParaId, + msg: &DownwardMessage, + ) -> Result<(), QueueDownwardMessageError> { + let serialized_len = msg.len() as u32; + if serialized_len > config.max_downward_message_size { + return Err(QueueDownwardMessageError::ExceedsMaxMessageSize) + } + Ok(()) + } + /// Enqueue a downward message to a specific recipient para. /// /// When encoded, the message should not exceed the `config.max_downward_message_size`. diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index bbbea1dedee6..dc0b7f18cf79 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -41,7 +41,8 @@ pub type LocalOriginToLocation = ( pub struct DoNothingRouter; impl SendXcm for DoNothingRouter { - fn send_xcm(_dest: &mut Option, _msg: &mut Option>) -> SendResult { + type OptionTicket = Option<()>; + fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult { Ok(()) } } diff --git a/xcm/pallet-xcm-benchmarks/src/mock.rs b/xcm/pallet-xcm-benchmarks/src/mock.rs index 4d23175bdec3..eab2abbd8808 100644 --- a/xcm/pallet-xcm-benchmarks/src/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/mock.rs @@ -21,9 +21,11 @@ use xcm_executor::traits::FilterAssetLocation; // An xcm sender/receiver akin to > /dev/null pub struct DevNull; impl xcm::opaque::latest::SendXcm for DevNull { - fn send_xcm(_: &mut Option, _: &mut Option>) -> SendResult { - Ok(()) + type OptionTicket = Option<()>; + fn validate(_: &mut Option, _: &mut Option>) -> SendResult<()> { + Ok(((), MultiAssets::new())) } + fn deliver(_: ()) -> Result<(), SendError> { Ok(()) } } impl xcm_executor::traits::OnResponse for DevNull { diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index e53b0e245b58..7f6e0ae161ff 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -1054,7 +1054,8 @@ pub mod pallet { let message = Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]); let event = match send_xcm::(new_key.clone(), message) { - Ok(()) => { + Ok(_cost) => { + // TODO: consider charging for cost. let value = (query_id, max_weight, xcm_version); VersionNotifyTargets::::insert(XCM_VERSION, key, value); Event::VersionChangeNotified(new_key, xcm_version) @@ -1105,7 +1106,8 @@ pub mod pallet { querier: None, }]); let event = match send_xcm::(new_key.clone(), message) { - Ok(()) => { + Ok(_cost) => { + // TODO: consider accounting for cost. VersionNotifyTargets::::insert( XCM_VERSION, versioned_key, @@ -1160,12 +1162,13 @@ pub mod pallet { } /// Relay an XCM `message` from a given `interior` location in this context to a given `dest` - /// location. A null `dest` is not handled. + /// location. A `dest` of `Here` is not handled. The overall price of the delivery is + /// returned. pub fn send_xcm( interior: impl Into, dest: impl Into, mut message: Xcm<()>, - ) -> Result<(), SendError> { + ) -> Result { let interior = interior.into(); let dest = dest.into(); if interior != Junctions::Here { diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 0128f0fb3ec2..f23497353812 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -156,8 +156,15 @@ pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { /// Sender that never returns error, always sends pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, MultiAssets::new())) + } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } @@ -165,15 +172,22 @@ impl SendXcm for TestSendXcm { /// Sender that returns error if `X8` junction and stops routing pub struct TestSendXcmErrX8; impl SendXcm for TestSendXcmErrX8 { - fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { let (dest, msg) = (dest.take().unwrap(), msg.take().unwrap()); if dest.len() == 8 { Err(SendError::Transport("Destination location full")) } else { - SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); - Ok(()) + Ok(((dest, msg), MultiAssets::new())) } } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(()) + } } parameter_types! { diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index c7da7347ee7b..be67e15d91c6 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -44,8 +44,8 @@ pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; pub use traits::{ - send_xcm, Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendCostResult, SendError, - SendResult, SendXcm, Weight, + Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, + SendResult, SendXcm, Weight, send_xcm, validate_send, Unwrappable, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -165,7 +165,7 @@ impl From> for Vec> { pub mod prelude { mod contents { pub use super::super::{ - send_xcm, Ancestor, AncestorThen, + Ancestor, AncestorThen, AssetId::{self, *}, AssetInstance::{self, *}, BodyId, BodyPart, Error as XcmError, ExecuteXcm, @@ -179,8 +179,8 @@ pub mod prelude { MultiAssets, MultiLocation, NetworkId::{self, *}, OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, - QueryResponseInfo, Response, Result as XcmResult, SendCostResult, SendError, - SendResult, SendXcm, + QueryResponseInfo, Response, Result as XcmResult, SendError, + SendResult, SendXcm, send_xcm, validate_send, Unwrappable, WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 9f69baf269ca..f23c5c3cf7ad 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -315,10 +315,21 @@ pub enum SendError { } /// Result value when attempting to send an XCM message. -pub type SendResult = result::Result<(), SendError>; +pub type SendResult = result::Result<(T, MultiAssets), SendError>; -/// Result value when attempting to determine the cost for sending an XCM message. -pub type SendCostResult = result::Result; +pub trait Unwrappable { + type Inner; + fn none() -> Self; + fn some(i: Self::Inner) -> Self; + fn take(self) -> Option; +} + +impl Unwrappable for Option { + type Inner = T; + fn none() -> Self { None } + fn some(i: Self::Inner) -> Self { Some(i) } + fn take(self) -> Option { self } +} /// Utility for sending an XCM message. /// @@ -382,59 +393,83 @@ pub type SendCostResult = result::Result; /// # } /// ``` pub trait SendXcm { - /// Determine the cost which will be paid by this chain if the XCM `message` is sent to the - /// given `destination`. - /// - /// If it is not a destination which can be reached with this type but possibly could by others, - /// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple - /// implementation to exit early without trying other type fields. - fn send_cost(_: &MultiLocation, _: &Xcm<()>) -> SendCostResult { - Ok(MultiAssets::new()) - } + type OptionTicket: Unwrappable; - /// Send an XCM `message` to a given `destination`, paying whatever must be paid for the message - /// to be delivered. + /// Check whether the given `_message` is deliverable to the given `_destination` and if so + /// determine the cost which will be paid by this chain to do so, returning a `Validated` token + /// which can be used to enact delivery. /// /// The `destination` and `message` must be `Some` (or else an error will be returned) and they /// may only be consumed if the `Err` is not `CannotReachDestination`. /// /// If it is not a destination which can be reached with this type but possibly could by others, - /// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple + /// then this *MUST* return `CannotReachDestination`. Any other error will cause the tuple /// implementation to exit early without trying other type fields. - fn send_xcm( + fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult; + ) -> SendResult<::Inner>; + + /// Actually carry out the delivery operation for a previously validated message sending. + fn deliver(ticket: ::Inner) -> result::Result<(), SendError>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl SendXcm for Tuple { - fn send_cost(destination: &MultiLocation, message: &Xcm<()>) -> SendCostResult { - for_tuples!( #( - match Tuple::send_cost(destination, message) { - Err(SendError::CannotReachDestination) => {}, - o @ _ => return o, - }; - )* ); - Err(SendError::CannotReachDestination) - } + type OptionTicket = Option<( for_tuples!{ #( Tuple::OptionTicket ),* } )>; - fn send_xcm( + fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult { + ) -> SendResult<( for_tuples!{ #( Tuple::OptionTicket ),* } )> { + let mut maybe_cost: Option = None; + let one_ticket: ( for_tuples!{ #( Tuple::OptionTicket ),* } ) = ( for_tuples!{ #( + if maybe_cost.is_some() { + ::none() + } else { + match Tuple::validate(destination, message) { + Err(SendError::CannotReachDestination) => ::none(), + Err(e) => { return Err(e) }, + Ok((v, c)) => { + maybe_cost = Some(c); + ::some(v) + }, + } + } + ),* } ); + if let Some(cost) = maybe_cost { + Ok((one_ticket, cost)) + } else { + Err(SendError::CannotReachDestination) + } + } + + fn deliver(one_ticket: ::Inner) -> result::Result<(), SendError> { for_tuples!( #( - match Tuple::send_xcm(destination, message) { - Err(SendError::CannotReachDestination) => {}, - o @ _ => return o, - }; + if let Some(validated) = one_ticket.Tuple.take() { + return Tuple::deliver(validated); + } )* ); - Err(SendError::CannotReachDestination) + Err(SendError::Unroutable) } } /// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps /// both in `Some` before passing them as as mutable references into `T::send_xcm`. -pub fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> SendResult { - T::send_xcm(&mut Some(dest), &mut Some(msg)) +pub fn validate_send(dest: MultiLocation, msg: Xcm<()>) -> SendResult<::Inner> { + T::validate(&mut Some(dest), &mut Some(msg)) +} + +/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps +/// both in `Some` before passing them as as mutable references into `T::send_xcm`. +/// +/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message +/// could not be sent. +/// +/// Generally you'll want to validate and get the price first to ensure that the sender can pay it +/// before actually doing the delivery. +pub fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> result::Result { + let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?; + T::deliver(ticket)?; + Ok(price) } diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index 7afd2009a48b..f35409910353 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -42,7 +42,7 @@ type Router = LocalUnpaidExporter, Universal fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); - assert_eq!(send_xcm::(dest, msg), Ok(())); + assert_eq!(send_xcm::(dest, msg), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -70,7 +70,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -96,7 +96,7 @@ fn sending_to_parachain_of_bridged_chain_works() { #[test] fn sending_to_relay_chain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs index 193142d09214..f3929cb306f1 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -37,7 +37,7 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Remote::get()).into(), msg), Ok(())); + assert_eq!(send_xcm::((Parent, Remote::get()).into(), msg), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -58,7 +58,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index d5aa817f3f2c..c9b9435cf566 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -57,12 +57,16 @@ std::thread_local! { } struct TestRemoteIncomingRouter; impl SendXcm for TestRemoteIncomingRouter { - fn send_xcm( - destination: &mut Option, - message: &mut Option>, - ) -> SendResult { - let pair = (destination.take().unwrap(), message.take().unwrap()); - REMOTE_INCOMING_XCM.with(|r| r.borrow_mut().push(pair)); + type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { + let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, MultiAssets::new())) + } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { + REMOTE_INCOMING_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } } @@ -79,15 +83,21 @@ struct UnpaidExecutingRouter( impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { - fn send_xcm( + type OptionTicket = Option>; + + fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult { + ) -> SendResult> { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { return Err(CannotReachDestination) } let message = message.take().ok_or(MissingArgument)?; + Ok((message, MultiAssets::new())) + } + + fn deliver(message: Xcm<()>) -> Result<(), SendError> { // We now pretend that the message was delivered from `Local` to `Remote`, and execute // so we need to ensure that the `TestConfig` is set up properly for executing as // though it is `Remote`. @@ -113,15 +123,21 @@ struct ExecutingRouter(PhantomData<(Local, Remote impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { - fn send_xcm( + type OptionTicket = Option>; + + fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult { + ) -> SendResult> { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { return Err(CannotReachDestination) } let message = message.take().ok_or(MissingArgument)?; + Ok((message, MultiAssets::new())) + } + + fn deliver(message: Xcm<()>) -> Result<(), SendError> { // We now pretend that the message was delivered from `Local` to `Remote`, and execute // so we need to ensure that the `TestConfig` is set up properly for executing as // though it is `Remote`. @@ -132,7 +148,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S // The we execute it: let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); - return match outcome { + match outcome { Outcome::Complete(..) => Ok(()), Outcome::Incomplete(..) => Err(Transport("Error executing")), Outcome::Error(..) => Err(Transport("Unable to execute")), diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs index 9be8f3c96bac..792675e96db8 100644 --- a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -64,7 +64,7 @@ fn sending_to_bridged_chain_works() { add_asset(to_account(Parachain(100)).unwrap(), (Here, 100)); let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::(dest, msg), Ok(())); + assert_eq!(send_xcm::(dest, msg), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -104,7 +104,7 @@ fn sending_to_parachain_of_bridged_chain_works() { // Initialize the local relay so that our parachain has funds to pay for export. add_asset(to_account(Parachain(100)).unwrap(), (Here, 1000)); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(100).into(), diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index f580673e0b81..02571c45bd30 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -51,7 +51,7 @@ fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!( send_xcm::((Parent, Parent, Remote::get(), Parachain(1)).into(), msg), - Ok(()) + Ok(MultiAssets::new()) ); assert_eq!(TheBridge::service(), 1); assert_eq!( @@ -80,7 +80,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_sibling_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -106,7 +106,7 @@ fn sending_to_sibling_of_bridged_chain_works() { #[test] fn sending_to_relay_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index 174aaec0089a..0b5f2378e29f 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -49,7 +49,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), Ok(())); + assert_eq!(send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -70,7 +70,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_sibling_of_bridged_chain_works() { let dest = (Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -92,7 +92,7 @@ fn sending_to_sibling_of_bridged_chain_works() { #[test] fn sending_to_relay_of_bridged_chain_works() { let dest = (Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parent.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; assert_eq!(take_received_remote_messages(), expected); diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index 39885b0edcd0..6fa057348d84 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -49,7 +49,7 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Parent, Remote::get()).into(), msg), Ok(())); + assert_eq!(send_xcm::((Parent, Parent, Remote::get()).into(), msg), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -77,7 +77,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(1000).into(), diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 76d1e8a759c9..8dcf69e47f90 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -106,7 +106,7 @@ thread_local! { pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); pub static EXPORTED_XCM: RefCell)>> = RefCell::new(Vec::new()); pub static EXPORTER_OVERRIDE: RefCell, &mut Option>) -> SendResult + fn(NetworkId, u32, &mut Option, &mut Option>) -> Result<(), SendError> >> = RefCell::new(None); } pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { @@ -116,7 +116,7 @@ pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm EXPORTED_XCM.with(|q| (*q.borrow()).clone()) } pub fn set_exporter_override( - f: fn(NetworkId, u32, &mut Option, &mut Option>) -> SendResult, + f: fn(NetworkId, u32, &mut Option, &mut Option>) -> Result<(), SendError>, ) { EXPORTER_OVERRIDE.with(|x| x.replace(Some(f))); } @@ -126,8 +126,15 @@ pub fn clear_exporter_override() { } pub struct TestMessageSender; impl SendXcm for TestMessageSender { - fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, MultiAssets::new())) + } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } @@ -139,7 +146,7 @@ impl ExportXcm for TestMessageExporter { channel: u32, dest: &mut Option, msg: &mut Option>, - ) -> SendResult { + ) -> Result<(), SendError> { EXPORTER_OVERRIDE.with(|e| { if let Some(ref f) = &*e.borrow() { f(network, channel, dest, msg) diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index ff82d42e6eb5..0a560cb3bfe5 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -59,9 +59,9 @@ pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancest impl> SendXcm for LocalUnpaidExporter { - // TODO: include price for use of `Exporter` in `send_cost` impl. + type OptionTicket = Option<(NetworkId, InteriorMultiLocation, Xcm<()>)>; - fn send_xcm(dest: &mut Option, xcm: &mut Option>) -> SendResult { + fn validate(dest: &mut Option, xcm: &mut Option>) -> SendResult<(NetworkId, InteriorMultiLocation, Xcm<()>)> { let d = dest.take().ok_or(MissingArgument)?; let devolved = match ensure_is_remote(Ancestry::get(), d) { Ok(x) => x, @@ -78,6 +78,11 @@ impl> SendXcm message.inner_mut().push(DescendOrigin(local_location)); } message.inner_mut().extend(inner.into_iter()); + let price = Exporter::export_price(network, &destination, &message)?; + Ok(((network, destination, message), price)) + } + + fn deliver((network, destination, message): (NetworkId, InteriorMultiLocation, Xcm<()>)) -> Result<(), SendError> { Exporter::export_xcm(network, 0, &mut Some(destination), &mut Some(message)) } } @@ -86,7 +91,7 @@ pub trait ExporterFor { /// Return the locally-routable bridge (if any) capable of forwarding `message` to the /// `remote_location` on the remote `network`, together with the payment which is required. /// - /// The payment is specified from the context of the bridge, not the local chain. This is the# + /// The payment is specified from the local context, not the bridge chain. This is the /// total amount to withdraw in to Holding and should cover both payment for the execution on /// the bridge chain as well as payment for the use of the `ExportMessage` instruction. fn exporter_for( @@ -143,9 +148,12 @@ pub struct UnpaidRemoteExporter( impl> SendXcm for UnpaidRemoteExporter { - // TODO: include `Router::send_cost` for getting the `ExportMessage` to the bridge. + type OptionTicket = Router::OptionTicket; - fn send_xcm(dest: &mut Option, xcm: &mut Option>) -> SendResult { + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult<::Inner> { let d = dest.as_ref().ok_or(MissingArgument)?.clone(); let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| CannotReachDestination)?; let (remote_network, remote_location, local_network, local_location) = devolved; @@ -160,9 +168,9 @@ impl } exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); - let (bridge, payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) + let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) .ok_or(CannotReachDestination)?; - ensure!(payment.is_none(), Unroutable); + ensure!(maybe_payment.is_none(), Unroutable); // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message @@ -172,7 +180,15 @@ impl destination: remote_location, xcm: exported, }]); - send_xcm::(bridge, message) + let (v, mut cost) = validate_send::(bridge, message)?; + if let Some(payment) = maybe_payment { + cost.push(payment); + } + Ok((v, cost)) + } + + fn deliver(validation: ::Inner) -> Result<(), SendError> { + Router::deliver(validation) } } @@ -190,10 +206,12 @@ pub struct SovereignPaidRemoteExporter( impl> SendXcm for SovereignPaidRemoteExporter { - // TODO: include `Router::send_cost` for getting the `ExportMessage` to the bridge and the - // total cost paid at the bridge `Exporter::export_price` in the `send_cost` impl. + type OptionTicket = Router::OptionTicket; - fn send_xcm(dest: &mut Option, xcm: &mut Option>) -> SendResult { + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult<::Inner> { let d = dest.as_ref().ok_or(MissingArgument)?.clone(); let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| CannotReachDestination)?; let (remote_network, remote_location, local_network, local_location) = devolved; @@ -208,23 +226,20 @@ impl } exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); - let (bridge, maybe_payment) = - Bridges::exporter_for(&remote_network, &remote_location, &exported) - .ok_or(CannotReachDestination)?; + let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) + .ok_or(CannotReachDestination)?; let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge).map_err(|_| Unroutable)?; - - // We then send a normal message to the bridge asking it to export the prepended - // message to the remote chain. This will only work if the bridge will do the message - // export for free. Common-good chains will typically be afforded this. let export_instruction = ExportMessage { network: remote_network, destination: remote_location, xcm: exported }; - let message = Xcm(if let Some(payment) = maybe_payment { + let message = Xcm(if let Some(ref payment) = maybe_payment { + let fees = payment.clone().reanchored(&bridge, &Ancestry::get().into()) + .map_err(|_| Unroutable)?; vec![ - WithdrawAsset(payment.clone().into()), - BuyExecution { fees: payment, weight_limit: Unlimited }, + WithdrawAsset(fees.clone().into()), + BuyExecution { fees, weight_limit: Unlimited }, export_instruction, RefundSurplus, DepositAsset { assets: All.into(), beneficiary: local_from_bridge }, @@ -236,7 +251,15 @@ impl // We then send a normal message to the bridge asking it to export the prepended // message to the remote chain. This will only work if the bridge will do the message // export for free. Common-good chains will typically be afforded this. - send_xcm::(bridge, message) + let (v, mut cost) = validate_send::(bridge, message)?; + if let Some(bridge_payment) = maybe_payment { + cost.push(bridge_payment); + } + Ok((v, cost)) + } + + fn deliver(ticket: ::Inner) -> Result<(), SendError> { + Router::deliver(ticket) } } diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index e541ee0e44c6..2aa187a9400c 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -47,8 +47,15 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { } pub struct TestSendXcm; impl SendXcm for TestSendXcm { - fn send_xcm(dest: &mut Option, msg: &mut Option>) -> SendResult { + type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, MultiAssets::new())) + } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(()) } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 7c238099ef2f..35773841ac6d 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -319,7 +319,9 @@ impl XcmExecutor { assets.reanchor(&dest, &context).map_err(|()| XcmError::MultiLocationFull)?; let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - send_xcm::(dest, Xcm(message)).map_err(Into::into) + let _cost = send_xcm::(dest, Xcm(message))?; + // TODO: pre-charge cost using new API. + Ok(()) }, ReceiveTeleportedAsset(assets) => { let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); @@ -422,7 +424,9 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest, None); let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - send_xcm::(dest, Xcm(message)).map_err(Into::into) + let _cost = send_xcm::(dest, Xcm(message))?; + // TODO: pre-charge cost using new API. + Ok(()) }, InitiateReserveWithdraw { assets, reserve, xcm } => { // Note that here we are able to place any assets which could not be reanchored @@ -434,7 +438,9 @@ impl XcmExecutor { ); let mut message = vec![WithdrawAsset(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - send_xcm::(reserve, Xcm(message)).map_err(Into::into) + let _cost = send_xcm::(reserve, Xcm(message))?; + // TODO: pre-charge cost using new API. + Ok(()) }, InitiateTeleport { assets, dest, xcm } => { // We must do this first in order to resolve wildcards. @@ -447,7 +453,9 @@ impl XcmExecutor { let assets = Self::reanchored(assets, &dest, None); let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - send_xcm::(dest, Xcm(message)).map_err(Into::into) + let _cost = send_xcm::(dest, Xcm(message))?; + // TODO: pre-charge cost using new API. + Ok(()) }, ReportHolding { response_info, assets } => { // Note that we pass `None` as `maybe_failed_bin` since no assets were ever removed @@ -545,7 +553,9 @@ impl XcmExecutor { let querier = Self::to_querier(self.origin.clone(), &destination)?; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - send_xcm::(destination, message).map_err(Into::into) + let _cost = send_xcm::(destination, message)?; + // TODO: pre-charge cost using new API. + Ok(()) }, ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => { let pallet = Config::PalletInstancesInfo::infos() @@ -626,7 +636,9 @@ impl XcmExecutor { let QueryResponseInfo { destination, query_id, max_weight } = info; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - send_xcm::(destination, message).map_err(Into::into) + let _cost = send_xcm::(destination, message)?; + // TODO: pre-charge cost using new API. + Ok(()) } /// NOTE: Any assets which were unable to be reanchored are introduced into `failed_bin`. diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index f0296090e54b..58750cbdbfd3 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -22,7 +22,7 @@ pub trait ExportXcm { _network: NetworkId, _destination: &InteriorMultiLocation, _message: &Xcm<()>, - ) -> SendCostResult { + ) -> Result { Ok(MultiAssets::new()) } @@ -31,7 +31,7 @@ pub trait ExportXcm { channel: u32, destination: &mut Option, message: &mut Option>, - ) -> SendResult; + ) -> Result<(), SendError>; } #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -40,7 +40,7 @@ impl ExportXcm for Tuple { network: NetworkId, dest: &InteriorMultiLocation, message: &Xcm<()>, - ) -> SendCostResult { + ) -> Result { for_tuples!( #( match Tuple::export_price(network, dest, message) { Err(SendError::CannotReachDestination) => {}, @@ -55,7 +55,7 @@ impl ExportXcm for Tuple { channel: u32, dest: &mut Option, message: &mut Option>, - ) -> SendResult { + ) -> Result<(), SendError> { for_tuples!( #( match Tuple::export_xcm(network, channel, dest, message) { Err(SendError::CannotReachDestination) => {}, @@ -73,6 +73,6 @@ pub fn export_xcm( channel: u32, dest: InteriorMultiLocation, message: Xcm<()>, -) -> SendResult { +) -> Result<(), SendError> { T::export_xcm(network, channel, &mut Some(dest), &mut Some(message)) } diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index c4aa3adc3a29..b04daff4a171 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -296,7 +296,11 @@ macro_rules! decl_test_network { pub struct ParachainXcmRouter($crate::PhantomData); impl> $crate::SendXcm for ParachainXcmRouter { - fn send_xcm(destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>) -> $crate::SendResult { + type OptionTicket = Option<($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>)>; + fn validate( + destination: &mut Option<$crate::MultiLocation>, + message: &mut Option<$crate::Xcm<()>>, + ) -> $crate::SendResult<($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>)> { use $crate::{UmpSink, XcmpMessageHandlerT}; let d = destination.take().ok_or($crate::SendError::MissingArgument)?; @@ -311,7 +315,12 @@ macro_rules! decl_test_network { }, } let m = message.take().ok_or($crate::SendError::MissingArgument)?; - $crate::PARA_MESSAGE_BUS.with(|b| b.borrow_mut().push_back((T::get(), d, m))); + Ok(((T::get(), d, m), $crate::MultiAssets::new())) + } + fn deliver( + triple: ($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>), + ) -> Result<(), $crate::SendError> { + $crate::PARA_MESSAGE_BUS.with(|b| b.borrow_mut().push_back(triple)); Ok(()) } } @@ -319,7 +328,11 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; impl $crate::SendXcm for RelayChainXcmRouter { - fn send_xcm(destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>) -> $crate::SendResult { + type OptionTicket = Option<($crate::MultiLocation, $crate::Xcm<()>)>; + fn validate( + destination: &mut Option<$crate::MultiLocation>, + message: &mut Option<$crate::Xcm<()>>, + ) -> $crate::SendResult<($crate::MultiLocation, $crate::Xcm<()>)> { use $crate::DmpMessageHandlerT; let d = destination.take().ok_or($crate::SendError::MissingArgument)?; @@ -333,7 +346,12 @@ macro_rules! decl_test_network { }, } let m = message.take().ok_or($crate::SendError::MissingArgument)?; - $crate::RELAY_MESSAGE_BUS.with(|b| b.borrow_mut().push_back((d, m))); + Ok(((d, m), $crate::MultiAssets::new())) + } + fn deliver( + pair: ($crate::MultiLocation, $crate::Xcm<()>), + ) -> Result<(), $crate::SendError> { + $crate::RELAY_MESSAGE_BUS.with(|b| b.borrow_mut().push_back(pair)); Ok(()) } } From 450f529db3321b46bde79477e640563982f24b8e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 5 Feb 2022 16:31:14 +0000 Subject: [PATCH 42/57] Fix tests --- .../src/bridging_tests/paid_remote_relay_relay.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs index 792675e96db8..14471ee01989 100644 --- a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -27,8 +27,8 @@ parameter_types! { pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100)); pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); - pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> - = vec![(Remote::get(), MultiLocation::parent(), Some((Here, 100).into()))]; + pub static BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 100).into()))]; } type TheBridge = TestBridge>; @@ -64,7 +64,7 @@ fn sending_to_bridged_chain_works() { add_asset(to_account(Parachain(100)).unwrap(), (Here, 100)); let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::(dest, msg), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg), Ok((Parent, 100).into())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -104,7 +104,7 @@ fn sending_to_parachain_of_bridged_chain_works() { // Initialize the local relay so that our parachain has funds to pay for export. add_asset(to_account(Parachain(100)).unwrap(), (Here, 1000)); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Parent, 100).into())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(100).into(), From 7525c01b10cd667dc63879742d1fe9102a6ae909 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 7 Feb 2022 19:02:55 +0000 Subject: [PATCH 43/57] Refactoring of SendXcm and ExportXcm complete --- runtime/common/src/xcm_sender.rs | 11 +- runtime/test-runtime/src/xcm_config.rs | 5 +- xcm/src/v3/multiasset.rs | 2 +- xcm/src/v3/traits.rs | 34 ++++-- .../src/bridging_tests/local_para_para.rs | 8 +- .../src/bridging_tests/local_relay_relay.rs | 6 +- xcm/xcm-builder/src/bridging_tests/mod.rs | 27 ++++- .../bridging_tests/paid_remote_relay_relay.rs | 16 +-- .../src/bridging_tests/remote_para_para.rs | 2 +- .../remote_para_para_via_relay.rs | 2 +- .../src/bridging_tests/remote_relay_relay.rs | 2 +- xcm/xcm-builder/src/mock.rs | 41 +++++-- xcm/xcm-builder/src/universal_exports.rs | 32 +++--- xcm/xcm-executor/src/lib.rs | 13 +-- xcm/xcm-executor/src/traits/export.rs | 107 ++++++++++++------ xcm/xcm-executor/src/traits/mod.rs | 2 +- 16 files changed, 204 insertions(+), 106 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index e7a6d8bcec10..b67aa3f30be0 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -19,7 +19,7 @@ use parity_scale_codec::Encode; use primitives::v1::Id as ParaId; use runtime_parachains::{configuration::{self, HostConfiguration}, dmp}; -use sp_std::marker::PhantomData; +use sp_std::{prelude::*, marker::PhantomData}; use xcm::prelude::*; use SendError::*; @@ -31,7 +31,7 @@ impl SendXcm { type OptionTicket = Option<(HostConfiguration, ParaId, Vec)>; - fn validate(dest: &mut Option, msg: &mut Option>) -> SendResult { + fn validate(dest: &mut Option, msg: &mut Option>) -> SendResult<(HostConfiguration, ParaId, Vec)> { let d = dest.take().ok_or(MissingArgument)?; let id = if let MultiLocation { parents: 0, interior: X1(Parachain(id)) } = &d { *id @@ -51,9 +51,8 @@ impl SendXcm Ok(((config, para, blob), MultiAssets::new())) } - fn deliver((config, para, blob): (HostConfiguration, ParaId, VersionedXcm<()>)) -> Result<(), SendError> { - let r = >::queue_downward_message(&config, para, blob); - debug_assert!(r.is_ok(), "Unexpected error in DMP delivery {:?}", r); - Ok(()) + fn deliver((config, para, blob): (HostConfiguration, ParaId, Vec)) -> Result<(), SendError> { + >::queue_downward_message(&config, para, blob) + .map_err(|_| SendError::Transport(&"Error placing into DMP queue")) } } diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index dc0b7f18cf79..332860cb7956 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -42,7 +42,10 @@ pub type LocalOriginToLocation = ( pub struct DoNothingRouter; impl SendXcm for DoNothingRouter { type OptionTicket = Option<()>; - fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult { + fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult<()> { + Ok(((), MultiAssets::new())) + } + fn deliver(_: ()) -> Result<(), SendError> { Ok(()) } } diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index a81f75c34078..83cd37109f6d 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -181,7 +181,7 @@ impl TryFrom for MultiAsset { } /// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, Default)] pub struct MultiAssets(Vec); impl Decode for MultiAssets { diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index f23c5c3cf7ad..438e3ad0bfd6 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -342,38 +342,48 @@ impl Unwrappable for Option { /// ```rust /// # use xcm::v3::prelude::*; /// # use parity_scale_codec::Encode; +/// # use std::convert::Infallible; /// /// /// A sender that only passes the message through and does nothing. /// struct Sender1; /// impl SendXcm for Sender1 { -/// fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { -/// return Err(SendError::CannotReachDestination) +/// type OptionTicket = Option; +/// fn validate(_: &mut Option, _: &mut Option>) -> SendResult { +/// Err(SendError::CannotReachDestination) +/// } +/// fn deliver(_: Infallible) -> Result<(), SendError> { +/// unreachable!() /// } /// } /// /// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing. /// struct Sender2; /// impl SendXcm for Sender2 { -/// fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { -/// let d = destination.as_ref().ok_or(SendError::MissingArgument)?; -/// if let MultiLocation { parents: 0, interior: X2(j1, j2) } = d { -/// Ok(()) -/// } else { -/// Err(SendError::Unroutable) +/// type OptionTicket = Option<()>; +/// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { +/// match destination.as_ref().ok_or(SendError::MissingArgument)? { +/// MultiLocation { parents: 0, interior: X2(j1, j2) } => Ok(((), MultiAssets::new())), +/// _ => Err(SendError::Unroutable), /// } /// } +/// fn deliver(_: ()) -> Result<(), SendError> { +/// Ok(()) +/// } /// } /// /// /// A sender that accepts a message from a parent, passing through otherwise. /// struct Sender3; /// impl SendXcm for Sender3 { -/// fn send_xcm(destination: &mut Option, message: &mut Option>) -> SendResult { -/// let d = destination.as_ref().ok_or(SendError::MissingArgument)?; -/// match d { -/// MultiLocation { parents: 1, interior: Here } => Ok(()), +/// type OptionTicket = Option<()>; +/// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { +/// match destination.as_ref().ok_or(SendError::MissingArgument)? { +/// MultiLocation { parents: 1, interior: Here } => Ok(((), MultiAssets::new())), /// _ => Err(SendError::CannotReachDestination), /// } /// } +/// fn deliver(_: ()) -> Result<(), SendError> { +/// Ok(()) +/// } /// } /// /// // A call to send via XCM. We don't really care about this. diff --git a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs index f35409910353..0e6bcfd4b6dd 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_para_para.rs @@ -26,7 +26,7 @@ parameter_types! { } type TheBridge = TestBridge>; -type Router = LocalUnpaidExporter, UniversalLocation>; +type Router = LocalUnpaidExporter, UniversalLocation>; /// ```nocompile /// local | remote @@ -42,7 +42,7 @@ type Router = LocalUnpaidExporter, Universal fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); - assert_eq!(send_xcm::(dest, msg), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg), Ok((Here, 100).into())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -70,7 +70,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Here, 100).into())); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -96,7 +96,7 @@ fn sending_to_parachain_of_bridged_chain_works() { #[test] fn sending_to_relay_chain_of_bridged_chain_works() { let dest = (Parent, Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Here, 100).into())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs index f3929cb306f1..244458f363fd 100644 --- a/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/local_relay_relay.rs @@ -26,7 +26,7 @@ parameter_types! { } type TheBridge = TestBridge>; -type Router = LocalUnpaidExporter, UniversalLocation>; +type Router = LocalUnpaidExporter, UniversalLocation>; /// ```nocompile /// local | remote @@ -37,7 +37,7 @@ type Router = LocalUnpaidExporter, Universal #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Remote::get()).into(), msg), Ok(MultiAssets::new())); + assert_eq!(send_xcm::((Parent, Remote::get()).into(), msg), Ok((Here, 100).into())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -58,7 +58,7 @@ fn sending_to_bridged_chain_works() { #[test] fn sending_to_parachain_of_bridged_chain_works() { let dest = (Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Here, 100).into())); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index c9b9435cf566..792ee8a3b126 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -20,7 +20,7 @@ use crate::{mock::*, universal_exports::*}; use frame_support::{parameter_types, traits::Get}; use std::{cell::RefCell, marker::PhantomData}; use xcm::prelude::*; -use xcm_executor::XcmExecutor; +use xcm_executor::{XcmExecutor, traits::{validate_export, export_xcm}}; use SendError::*; mod local_para_para; @@ -33,6 +33,7 @@ mod remote_relay_relay; parameter_types! { pub Local: NetworkId = ByGenesis([0; 32]); pub Remote: NetworkId = ByGenesis([1; 32]); + pub Price: MultiAssets = MultiAssets::from((Here, 100)); } std::thread_local! { @@ -80,6 +81,26 @@ fn take_received_remote_messages() -> Vec<(MultiLocation, Xcm<()>)> { struct UnpaidExecutingRouter( PhantomData<(Local, Remote, RemoteExporter)>, ); + +fn price( + n: NetworkId, + c: u32, + d: &InteriorMultiLocation, + m: &Xcm<()>, +) -> Result { + Ok(validate_export::(n, c, d.clone(), m.clone())?.1) +} + +fn deliver( + n: NetworkId, + c: u32, + d: InteriorMultiLocation, + m: Xcm<()>, +) -> Result<(), SendError> { + export_xcm::(n, c, d, m)?; + Ok(()) +} + impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { @@ -104,7 +125,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S ExecutorUniversalLocation::set(Remote::get()); let origin = Local::get().relative_to(&Remote::get()); AllowUnpaidFrom::set(vec![origin.clone()]); - set_exporter_override(RemoteExporter::export_xcm); + set_exporter_override(price::, deliver::); // The we execute it: let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); @@ -144,7 +165,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S ExecutorUniversalLocation::set(Remote::get()); let origin = Local::get().relative_to(&Remote::get()); AllowPaidFrom::set(vec![origin.clone()]); - set_exporter_override(RemoteExporter::export_xcm); + set_exporter_override(price::, deliver::); // The we execute it: let outcome = XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); diff --git a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs index 14471ee01989..cf70b56b4a87 100644 --- a/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/paid_remote_relay_relay.rs @@ -28,11 +28,13 @@ parameter_types! { pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); pub static BridgeTable: Vec<(NetworkId, MultiLocation, Option)> - = vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 100).into()))]; + = vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 150).into()))]; + // ^^^ 100 to use the bridge (export) and 50 for the remote execution weight (5 instuctions + // x 10 weight each). } type TheBridge = TestBridge>; -type RelayExporter = HaulBlobExporter; +type RelayExporter = HaulBlobExporter; type LocalInnerRouter = ExecutingRouter; type LocalBridgeRouter = SovereignPaidRemoteExporter< NetworkExportTable, @@ -61,10 +63,10 @@ fn sending_to_bridged_chain_works() { ); // Initialize the local relay so that our parachain has funds to pay for export. - add_asset(to_account(Parachain(100)).unwrap(), (Here, 100)); + add_asset(to_account(Parachain(100)).unwrap(), (Here, 1000)); let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::(dest, msg), Ok((Parent, 100).into())); + assert_eq!(send_xcm::(dest, msg), Ok((Parent, 150).into())); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -79,7 +81,7 @@ fn sending_to_bridged_chain_works() { ); // The export cost 50 weight units (and thus 50 units of balance). - assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 50).into()]); + assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 850).into()]); } /// ```nocompile @@ -104,7 +106,7 @@ fn sending_to_parachain_of_bridged_chain_works() { // Initialize the local relay so that our parachain has funds to pay for export. add_asset(to_account(Parachain(100)).unwrap(), (Here, 1000)); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Parent, 100).into())); + assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Parent, 150).into())); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(100).into(), @@ -117,5 +119,5 @@ fn sending_to_parachain_of_bridged_chain_works() { assert_eq!(take_received_remote_messages(), expected); // The export cost 50 weight units (and thus 50 units of balance). - assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 950).into()]); + assert_eq!(assets(to_account(Parachain(100)).unwrap()), vec![(Here, 850).into()]); } diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs index 02571c45bd30..a79c1efa23ba 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para.rs @@ -29,7 +29,7 @@ parameter_types! { } type TheBridge = TestBridge>; -type RelayExporter = HaulBlobExporter; +type RelayExporter = HaulBlobExporter; type LocalInnerRouter = UnpaidExecutingRouter; type LocalBridgingRouter = diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index 0b5f2378e29f..52d439fe8920 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -29,7 +29,7 @@ parameter_types! { } type TheBridge = TestBridge>; -type RelayExporter = HaulBlobExporter; +type RelayExporter = HaulBlobExporter; type LocalInnerRouter = UnpaidExecutingRouter; type LocalBridgingRouter = diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index 6fa057348d84..6113d323ff42 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -29,7 +29,7 @@ parameter_types! { } type TheBridge = TestBridge>; -type RelayExporter = HaulBlobExporter; +type RelayExporter = HaulBlobExporter; type LocalInnerRouter = UnpaidExecutingRouter; type LocalBridgeRouter = diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 8dcf69e47f90..ef065a1744de 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -105,9 +105,10 @@ impl GetDispatchInfo for TestCall { thread_local! { pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); pub static EXPORTED_XCM: RefCell)>> = RefCell::new(Vec::new()); - pub static EXPORTER_OVERRIDE: RefCell, &mut Option>) -> Result<(), SendError> - >> = RefCell::new(None); + pub static EXPORTER_OVERRIDE: RefCell) -> Result, + fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> Result<(), SendError>, + )>> = RefCell::new(None); } pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { SENT_XCM.with(|q| (*q.borrow()).clone()) @@ -116,9 +117,10 @@ pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm EXPORTED_XCM.with(|q| (*q.borrow()).clone()) } pub fn set_exporter_override( - f: fn(NetworkId, u32, &mut Option, &mut Option>) -> Result<(), SendError>, + price: fn(NetworkId, u32, &InteriorMultiLocation, &Xcm<()>) -> Result, + deliver: fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> Result<(), SendError>, ) { - EXPORTER_OVERRIDE.with(|x| x.replace(Some(f))); + EXPORTER_OVERRIDE.with(|x| x.replace(Some((price, deliver)))); } #[allow(dead_code)] pub fn clear_exporter_override() { @@ -141,18 +143,37 @@ impl SendXcm for TestMessageSender { } pub struct TestMessageExporter; impl ExportXcm for TestMessageExporter { - fn export_xcm( + type OptionTicket = Option<(NetworkId, u32, InteriorMultiLocation, Xcm<()>)>; + fn validate( network: NetworkId, channel: u32, dest: &mut Option, msg: &mut Option>, - ) -> Result<(), SendError> { + ) -> SendResult<(NetworkId, u32, InteriorMultiLocation, Xcm<()>)> { + let (d, m) = (dest.take().unwrap(), msg.take().unwrap()); + let r: Result = EXPORTER_OVERRIDE.with(|e| { + if let Some((ref f, _)) = &*e.borrow() { + f(network, channel, &d, &m) + } else { + Ok(MultiAssets::new()) + } + }); + match r { + Ok(price) => Ok(((network, channel, d, m), price)), + Err(e) => { + *dest = Some(d); + *msg = Some(m); + Err(e) + } + } + } + fn deliver(tuple: (NetworkId, u32, InteriorMultiLocation, Xcm<()>)) -> Result<(), SendError> { EXPORTER_OVERRIDE.with(|e| { - if let Some(ref f) = &*e.borrow() { + if let Some((_, ref f)) = &*e.borrow() { + let (network, channel, dest, msg) = tuple; f(network, channel, dest, msg) } else { - let (dest, msg) = (dest.take().unwrap(), msg.take().unwrap()); - EXPORTED_XCM.with(|q| q.borrow_mut().push((network, channel, dest, msg))); + EXPORTED_XCM.with(|q| q.borrow_mut().push(tuple)); Ok(()) } }) diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 0a560cb3bfe5..cd236b951556 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -20,7 +20,7 @@ use frame_support::{ensure, traits::Get}; use parity_scale_codec::{Decode, Encode}; use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; use xcm::prelude::*; -use xcm_executor::traits::ExportXcm; +use xcm_executor::traits::{ExportXcm, validate_export}; use SendError::*; fn ensure_is_remote( @@ -59,9 +59,9 @@ pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancest impl> SendXcm for LocalUnpaidExporter { - type OptionTicket = Option<(NetworkId, InteriorMultiLocation, Xcm<()>)>; + type OptionTicket = Exporter::OptionTicket; - fn validate(dest: &mut Option, xcm: &mut Option>) -> SendResult<(NetworkId, InteriorMultiLocation, Xcm<()>)> { + fn validate(dest: &mut Option, xcm: &mut Option>) -> SendResult<::Inner> { let d = dest.take().ok_or(MissingArgument)?; let devolved = match ensure_is_remote(Ancestry::get(), d) { Ok(x) => x, @@ -78,12 +78,11 @@ impl> SendXcm message.inner_mut().push(DescendOrigin(local_location)); } message.inner_mut().extend(inner.into_iter()); - let price = Exporter::export_price(network, &destination, &message)?; - Ok(((network, destination, message), price)) + validate_export::(network, 0, destination, message) } - fn deliver((network, destination, message): (NetworkId, InteriorMultiLocation, Xcm<()>)) -> Result<(), SendError> { - Exporter::export_xcm(network, 0, &mut Some(destination), &mut Some(message)) + fn deliver(ticket: ::Inner) -> Result<(), SendError> { + Exporter::deliver(ticket) } } @@ -319,18 +318,18 @@ impl> DispatchBlob } } -pub struct HaulBlobExporter(PhantomData<(Bridge, BridgedNetwork)>); -impl> ExportXcm - for HaulBlobExporter +pub struct HaulBlobExporter(PhantomData<(Bridge, BridgedNetwork, Price)>); +impl, Price: Get> ExportXcm + for HaulBlobExporter { - // TODO: Get impl for what value to return for `export_price`, then tests for it. + type OptionTicket = Option>; - fn export_xcm( + fn validate( network: NetworkId, _channel: u32, destination: &mut Option, message: &mut Option>, - ) -> Result<(), SendError> { + ) -> Result<(Vec, MultiAssets), SendError> { let bridged_network = BridgedNetwork::get(); ensure!(&network == &bridged_network, SendError::CannotReachDestination); // We don't/can't use the `channel` for this adapter. @@ -343,7 +342,12 @@ impl> ExportXcm }, }; let message = VersionedXcm::from(message.take().ok_or(SendError::MissingArgument)?); - Bridge::haul_blob(BridgeMessage { universal_dest, message }.encode()); + let blob = BridgeMessage { universal_dest, message }.encode(); + Ok((blob, Price::get())) + } + + fn deliver(blob: Vec) -> Result<(), SendError> { + Bridge::haul_blob(blob); Ok(()) } } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 35773841ac6d..3e548d6ea469 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -30,9 +30,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - export_xcm, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, + ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, - WeightTrader, + validate_export, WeightTrader, ExportXcm, }; mod assets; @@ -592,15 +592,14 @@ impl XcmExecutor { Ok(()) }, ExportMessage { network, destination, xcm } => { + let hash = (&self.origin, &destination).using_encoded(blake2_128); + let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); // Hash identifies the lane on the exporter which we use. We use the pairwise // combination of the origin and destination to ensure origin/destination pairs will // generally have their own lanes. - let fee = Config::MessageExporter::export_price(network, &destination, &xcm)?; + let (ticket, fee) = validate_export::(network, channel, destination, xcm)?; self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; - - let hash = (&self.origin, &destination).using_encoded(blake2_128); - let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); - export_xcm::(network, channel, destination, xcm)?; + Config::MessageExporter::deliver(ticket)?; Ok(()) }, ExchangeAsset { .. } => Err(XcmError::Unimplemented), diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index 58750cbdbfd3..7aee10e030d4 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -16,63 +16,102 @@ use xcm::latest::prelude::*; -/// Type which is able to send a given message over to another consensus network. pub trait ExportXcm { - fn export_price( - _network: NetworkId, - _destination: &InteriorMultiLocation, - _message: &Xcm<()>, - ) -> Result { - Ok(MultiAssets::new()) - } + type OptionTicket: Unwrappable; - fn export_xcm( + /// Check whether the given `_message` is deliverable to the given `_destination` and if so + /// determine the cost which will be paid by this chain to do so, returning a `Validated` token + /// which can be used to enact delivery. + /// + /// The `destination` and `message` must be `Some` (or else an error will be returned) and they + /// may only be consumed if the `Err` is not `CannotReachDestination`. + /// + /// If it is not a destination which can be reached with this type but possibly could by others, + /// then this *MUST* return `CannotReachDestination`. Any other error will cause the tuple + /// implementation to exit early without trying other type fields. + fn validate( network: NetworkId, channel: u32, destination: &mut Option, message: &mut Option>, - ) -> Result<(), SendError>; + ) -> SendResult<::Inner>; + + /// Actually carry out the delivery operation for a previously validated message sending. + /// + /// The implementation should do everything possible to ensure that this function is infallible + /// if called immediately after `validate`. Returning an error here would result in a price + /// paid without the service being delivered. + fn deliver(ticket: ::Inner) -> Result<(), SendError>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl ExportXcm for Tuple { - fn export_price( - network: NetworkId, - dest: &InteriorMultiLocation, - message: &Xcm<()>, - ) -> Result { - for_tuples!( #( - match Tuple::export_price(network, dest, message) { - Err(SendError::CannotReachDestination) => {}, - o @ _ => return o, - }; - )* ); - Err(SendError::CannotReachDestination) - } + type OptionTicket = Option<( for_tuples!{ #( Tuple::OptionTicket ),* } )>; - fn export_xcm( + fn validate( network: NetworkId, channel: u32, - dest: &mut Option, + destination: &mut Option, message: &mut Option>, - ) -> Result<(), SendError> { + ) -> SendResult<( for_tuples!{ #( Tuple::OptionTicket ),* } )> { + let mut maybe_cost: Option = None; + let one_ticket: ( for_tuples!{ #( Tuple::OptionTicket ),* } ) = ( for_tuples!{ #( + if maybe_cost.is_some() { + ::none() + } else { + match Tuple::validate(network, channel, destination, message) { + Err(SendError::CannotReachDestination) => ::none(), + Err(e) => { return Err(e) }, + Ok((v, c)) => { + maybe_cost = Some(c); + ::some(v) + }, + } + } + ),* } ); + if let Some(cost) = maybe_cost { + Ok((one_ticket, cost)) + } else { + Err(SendError::CannotReachDestination) + } + } + + fn deliver(one_ticket: ::Inner) -> Result<(), SendError> { for_tuples!( #( - match Tuple::export_xcm(network, channel, dest, message) { - Err(SendError::CannotReachDestination) => {}, - o @ _ => return o, - }; + if let Some(validated) = one_ticket.Tuple.take() { + return Tuple::deliver(validated); + } )* ); - Err(SendError::CannotReachDestination) + Err(SendError::Unroutable) } } /// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps /// both in `Some` before passing them as as mutable references into `T::send_xcm`. +pub fn validate_export( + network: NetworkId, + channel: u32, + dest: InteriorMultiLocation, + msg: Xcm<()>, +) -> SendResult<::Inner> { + T::validate(network, channel, &mut Some(dest), &mut Some(msg)) +} + +/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps +/// both in `Some` before passing them as as mutable references into `T::send_xcm`. +/// +/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message +/// could not be sent. +/// +/// Generally you'll want to validate and get the price first to ensure that the sender can pay it +/// before actually doing the delivery. pub fn export_xcm( network: NetworkId, channel: u32, dest: InteriorMultiLocation, - message: Xcm<()>, -) -> Result<(), SendError> { - T::export_xcm(network, channel, &mut Some(dest), &mut Some(message)) + msg: Xcm<()>, +) -> Result { + let (ticket, price) = T::validate(network, channel, &mut Some(dest), &mut Some(msg))?; + T::deliver(ticket)?; + Ok(price) } diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index a51854612bf0..dd00b48cbd63 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -23,7 +23,7 @@ pub use conversion::{ mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; -pub use export::{export_xcm, ExportXcm}; +pub use export::{export_xcm, ExportXcm, validate_export}; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; From e715ef258a566af10296de4458fc5705f8ee1621 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 7 Feb 2022 19:03:12 +0000 Subject: [PATCH 44/57] Formatting --- runtime/common/src/xcm_sender.rs | 16 ++++++-- xcm/pallet-xcm-benchmarks/src/mock.rs | 4 +- xcm/src/v3/mod.rs | 10 ++--- xcm/src/v3/traits.rs | 37 +++++++++++++------ xcm/xcm-builder/src/bridging_tests/mod.rs | 5 ++- .../remote_para_para_via_relay.rs | 5 ++- .../src/bridging_tests/remote_relay_relay.rs | 5 ++- xcm/xcm-builder/src/mock.rs | 2 +- xcm/xcm-builder/src/universal_exports.rs | 25 +++++++++---- xcm/xcm-executor/src/lib.rs | 9 +++-- xcm/xcm-executor/src/traits/export.rs | 8 ++-- xcm/xcm-executor/src/traits/mod.rs | 2 +- 12 files changed, 86 insertions(+), 42 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index b67aa3f30be0..1255559894e9 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -18,8 +18,11 @@ use parity_scale_codec::Encode; use primitives::v1::Id as ParaId; -use runtime_parachains::{configuration::{self, HostConfiguration}, dmp}; -use sp_std::{prelude::*, marker::PhantomData}; +use runtime_parachains::{ + configuration::{self, HostConfiguration}, + dmp, +}; +use sp_std::{marker::PhantomData, prelude::*}; use xcm::prelude::*; use SendError::*; @@ -31,7 +34,10 @@ impl SendXcm { type OptionTicket = Option<(HostConfiguration, ParaId, Vec)>; - fn validate(dest: &mut Option, msg: &mut Option>) -> SendResult<(HostConfiguration, ParaId, Vec)> { + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(HostConfiguration, ParaId, Vec)> { let d = dest.take().ok_or(MissingArgument)?; let id = if let MultiLocation { parents: 0, interior: X1(Parachain(id)) } = &d { *id @@ -51,7 +57,9 @@ impl SendXcm Ok(((config, para, blob), MultiAssets::new())) } - fn deliver((config, para, blob): (HostConfiguration, ParaId, Vec)) -> Result<(), SendError> { + fn deliver( + (config, para, blob): (HostConfiguration, ParaId, Vec), + ) -> Result<(), SendError> { >::queue_downward_message(&config, para, blob) .map_err(|_| SendError::Transport(&"Error placing into DMP queue")) } diff --git a/xcm/pallet-xcm-benchmarks/src/mock.rs b/xcm/pallet-xcm-benchmarks/src/mock.rs index eab2abbd8808..4892a04f9efa 100644 --- a/xcm/pallet-xcm-benchmarks/src/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/mock.rs @@ -25,7 +25,9 @@ impl xcm::opaque::latest::SendXcm for DevNull { fn validate(_: &mut Option, _: &mut Option>) -> SendResult<()> { Ok(((), MultiAssets::new())) } - fn deliver(_: ()) -> Result<(), SendError> { Ok(()) } + fn deliver(_: ()) -> Result<(), SendError> { + Ok(()) + } } impl xcm_executor::traits::OnResponse for DevNull { diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index be67e15d91c6..10a8b8ac0726 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -44,8 +44,8 @@ pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, }; pub use traits::{ - Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, - SendResult, SendXcm, Weight, send_xcm, validate_send, Unwrappable, + send_xcm, validate_send, Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, + SendResult, SendXcm, Unwrappable, Weight, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{BodyId, BodyPart, OriginKind, WeightLimit}; @@ -165,7 +165,7 @@ impl From> for Vec> { pub mod prelude { mod contents { pub use super::super::{ - Ancestor, AncestorThen, + send_xcm, validate_send, Ancestor, AncestorThen, AssetId::{self, *}, AssetInstance::{self, *}, BodyId, BodyPart, Error as XcmError, ExecuteXcm, @@ -179,8 +179,8 @@ pub mod prelude { MultiAssets, MultiLocation, NetworkId::{self, *}, OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, - QueryResponseInfo, Response, Result as XcmResult, SendError, - SendResult, SendXcm, send_xcm, validate_send, Unwrappable, + QueryResponseInfo, Response, Result as XcmResult, SendError, SendResult, SendXcm, + Unwrappable, WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 438e3ad0bfd6..052f2317f39a 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -326,9 +326,15 @@ pub trait Unwrappable { impl Unwrappable for Option { type Inner = T; - fn none() -> Self { None } - fn some(i: Self::Inner) -> Self { Some(i) } - fn take(self) -> Option { self } + fn none() -> Self { + None + } + fn some(i: Self::Inner) -> Self { + Some(i) + } + fn take(self) -> Option { + self + } } /// Utility for sending an XCM message. @@ -421,19 +427,20 @@ pub trait SendXcm { ) -> SendResult<::Inner>; /// Actually carry out the delivery operation for a previously validated message sending. - fn deliver(ticket: ::Inner) -> result::Result<(), SendError>; + fn deliver(ticket: ::Inner) + -> result::Result<(), SendError>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl SendXcm for Tuple { - type OptionTicket = Option<( for_tuples!{ #( Tuple::OptionTicket ),* } )>; + type OptionTicket = Option<(for_tuples! { #( Tuple::OptionTicket ),* })>; fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult<( for_tuples!{ #( Tuple::OptionTicket ),* } )> { + ) -> SendResult<(for_tuples! { #( Tuple::OptionTicket ),* })> { let mut maybe_cost: Option = None; - let one_ticket: ( for_tuples!{ #( Tuple::OptionTicket ),* } ) = ( for_tuples!{ #( + let one_ticket: (for_tuples! { #( Tuple::OptionTicket ),* }) = (for_tuples! { #( if maybe_cost.is_some() { ::none() } else { @@ -446,7 +453,7 @@ impl SendXcm for Tuple { }, } } - ),* } ); + ),* }); if let Some(cost) = maybe_cost { Ok((one_ticket, cost)) } else { @@ -454,7 +461,9 @@ impl SendXcm for Tuple { } } - fn deliver(one_ticket: ::Inner) -> result::Result<(), SendError> { + fn deliver( + one_ticket: ::Inner, + ) -> result::Result<(), SendError> { for_tuples!( #( if let Some(validated) = one_ticket.Tuple.take() { return Tuple::deliver(validated); @@ -466,7 +475,10 @@ impl SendXcm for Tuple { /// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps /// both in `Some` before passing them as as mutable references into `T::send_xcm`. -pub fn validate_send(dest: MultiLocation, msg: Xcm<()>) -> SendResult<::Inner> { +pub fn validate_send( + dest: MultiLocation, + msg: Xcm<()>, +) -> SendResult<::Inner> { T::validate(&mut Some(dest), &mut Some(msg)) } @@ -478,7 +490,10 @@ pub fn validate_send(dest: MultiLocation, msg: Xcm<()>) -> SendResul /// /// Generally you'll want to validate and get the price first to ensure that the sender can pay it /// before actually doing the delivery. -pub fn send_xcm(dest: MultiLocation, msg: Xcm<()>) -> result::Result { +pub fn send_xcm( + dest: MultiLocation, + msg: Xcm<()>, +) -> result::Result { let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?; T::deliver(ticket)?; Ok(price) diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index 792ee8a3b126..8704485004de 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -20,7 +20,10 @@ use crate::{mock::*, universal_exports::*}; use frame_support::{parameter_types, traits::Get}; use std::{cell::RefCell, marker::PhantomData}; use xcm::prelude::*; -use xcm_executor::{XcmExecutor, traits::{validate_export, export_xcm}}; +use xcm_executor::{ + traits::{export_xcm, validate_export}, + XcmExecutor, +}; use SendError::*; mod local_para_para; diff --git a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs index 52d439fe8920..3746e5c7766c 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_para_para_via_relay.rs @@ -49,7 +49,10 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), Ok(MultiAssets::new())); + assert_eq!( + send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), + Ok(MultiAssets::new()) + ); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), diff --git a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs index 6113d323ff42..0fed565a53f0 100644 --- a/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/bridging_tests/remote_relay_relay.rs @@ -49,7 +49,10 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Parent, Remote::get()).into(), msg), Ok(MultiAssets::new())); + assert_eq!( + send_xcm::((Parent, Parent, Remote::get()).into(), msg), + Ok(MultiAssets::new()) + ); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index ef065a1744de..cd879908ffbf 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -164,7 +164,7 @@ impl ExportXcm for TestMessageExporter { *dest = Some(d); *msg = Some(m); Err(e) - } + }, } } fn deliver(tuple: (NetworkId, u32, InteriorMultiLocation, Xcm<()>)) -> Result<(), SendError> { diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index cd236b951556..f09409d2e17a 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -20,7 +20,7 @@ use frame_support::{ensure, traits::Get}; use parity_scale_codec::{Decode, Encode}; use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; use xcm::prelude::*; -use xcm_executor::traits::{ExportXcm, validate_export}; +use xcm_executor::traits::{validate_export, ExportXcm}; use SendError::*; fn ensure_is_remote( @@ -61,7 +61,10 @@ impl> SendXcm { type OptionTicket = Exporter::OptionTicket; - fn validate(dest: &mut Option, xcm: &mut Option>) -> SendResult<::Inner> { + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult<::Inner> { let d = dest.take().ok_or(MissingArgument)?; let devolved = match ensure_is_remote(Ancestry::get(), d) { Ok(x) => x, @@ -167,8 +170,9 @@ impl } exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); - let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) - .ok_or(CannotReachDestination)?; + let (bridge, maybe_payment) = + Bridges::exporter_for(&remote_network, &remote_location, &exported) + .ok_or(CannotReachDestination)?; ensure!(maybe_payment.is_none(), Unroutable); // We then send a normal message to the bridge asking it to export the prepended @@ -225,8 +229,9 @@ impl } exported.inner_mut().extend(xcm.take().ok_or(MissingArgument)?.into_iter()); - let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) - .ok_or(CannotReachDestination)?; + let (bridge, maybe_payment) = + Bridges::exporter_for(&remote_network, &remote_location, &exported) + .ok_or(CannotReachDestination)?; let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge).map_err(|_| Unroutable)?; @@ -234,7 +239,9 @@ impl ExportMessage { network: remote_network, destination: remote_location, xcm: exported }; let message = Xcm(if let Some(ref payment) = maybe_payment { - let fees = payment.clone().reanchored(&bridge, &Ancestry::get().into()) + let fees = payment + .clone() + .reanchored(&bridge, &Ancestry::get().into()) .map_err(|_| Unroutable)?; vec![ WithdrawAsset(fees.clone().into()), @@ -318,7 +325,9 @@ impl> DispatchBlob } } -pub struct HaulBlobExporter(PhantomData<(Bridge, BridgedNetwork, Price)>); +pub struct HaulBlobExporter( + PhantomData<(Bridge, BridgedNetwork, Price)>, +); impl, Price: Get> ExportXcm for HaulBlobExporter { diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 3e548d6ea469..78ddd704ab92 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -30,9 +30,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, OnResponse, - ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, - validate_export, WeightTrader, ExportXcm, + validate_export, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, + OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, + WeightBounds, WeightTrader, }; mod assets; @@ -597,7 +597,8 @@ impl XcmExecutor { // Hash identifies the lane on the exporter which we use. We use the pairwise // combination of the origin and destination to ensure origin/destination pairs will // generally have their own lanes. - let (ticket, fee) = validate_export::(network, channel, destination, xcm)?; + let (ticket, fee) = + validate_export::(network, channel, destination, xcm)?; self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; Config::MessageExporter::deliver(ticket)?; Ok(()) diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index 7aee10e030d4..fe17d5d50c1e 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -46,16 +46,16 @@ pub trait ExportXcm { #[impl_trait_for_tuples::impl_for_tuples(30)] impl ExportXcm for Tuple { - type OptionTicket = Option<( for_tuples!{ #( Tuple::OptionTicket ),* } )>; + type OptionTicket = Option<(for_tuples! { #( Tuple::OptionTicket ),* })>; fn validate( network: NetworkId, channel: u32, destination: &mut Option, message: &mut Option>, - ) -> SendResult<( for_tuples!{ #( Tuple::OptionTicket ),* } )> { + ) -> SendResult<(for_tuples! { #( Tuple::OptionTicket ),* })> { let mut maybe_cost: Option = None; - let one_ticket: ( for_tuples!{ #( Tuple::OptionTicket ),* } ) = ( for_tuples!{ #( + let one_ticket: (for_tuples! { #( Tuple::OptionTicket ),* }) = (for_tuples! { #( if maybe_cost.is_some() { ::none() } else { @@ -68,7 +68,7 @@ impl ExportXcm for Tuple { }, } } - ),* } ); + ),* }); if let Some(cost) = maybe_cost { Ok((one_ticket, cost)) } else { diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index dd00b48cbd63..a8bfeea3d06d 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -23,7 +23,7 @@ pub use conversion::{ mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; -pub use export::{export_xcm, ExportXcm, validate_export}; +pub use export::{export_xcm, validate_export, ExportXcm}; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; From 59768bd210c8b0ed19f0059e566bff9a4cd74cb6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 7 Feb 2022 19:06:53 +0000 Subject: [PATCH 45/57] Rename CannotReachDestination -> NotApplicable --- runtime/common/src/xcm_sender.rs | 2 +- xcm/pallet-xcm/src/lib.rs | 2 +- xcm/src/v0/traits.rs | 14 +++++++------- xcm/src/v1/traits.rs | 14 +++++++------- xcm/src/v2/traits.rs | 16 ++++++++-------- xcm/src/v3/traits.rs | 18 +++++++++--------- xcm/xcm-builder/src/bridging_tests/mod.rs | 4 ++-- xcm/xcm-builder/src/universal_exports.rs | 14 +++++++------- xcm/xcm-executor/src/traits/export.rs | 8 ++++---- xcm/xcm-simulator/src/lib.rs | 4 ++-- 10 files changed, 48 insertions(+), 48 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 1255559894e9..57cae59474d7 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -43,7 +43,7 @@ impl SendXcm *id } else { *dest = Some(d); - return Err(CannotReachDestination) + return Err(NotApplicable) }; // Downward message passing. diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 7f6e0ae161ff..171320203e27 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -557,7 +557,7 @@ pub mod pallet { let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; Self::send_xcm(interior, dest.clone(), message.clone()).map_err(|e| match e { - SendError::CannotReachDestination => Error::::Unreachable, + SendError::NotApplicable => Error::::Unreachable, _ => Error::::SendFailure, })?; Self::deposit_event(Event::Sent(origin_location, dest, message)); diff --git a/xcm/src/v0/traits.rs b/xcm/src/v0/traits.rs index cfbc6a2e6a8f..aafc2820ff17 100644 --- a/xcm/src/v0/traits.rs +++ b/xcm/src/v0/traits.rs @@ -41,7 +41,7 @@ pub enum Error { /// A human-readable explanation of the specific issue is provided. SendFailed(#[codec(skip)] &'static str), /// The message and destination combination was not recognized as being reachable. - CannotReachDestination(MultiLocation, Xcm<()>), + NotApplicable(MultiLocation, Xcm<()>), MultiLocationFull, FailedToDecode, BadOrigin, @@ -180,7 +180,7 @@ impl ExecuteXcm for () { /// Utility for sending an XCM message. /// /// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return -/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination` +/// `NotApplicable` to pass the execution to the next sender item. Note that each `NotApplicable` /// might alter the destination and the XCM message for to the next router. /// /// @@ -193,7 +193,7 @@ impl ExecuteXcm for () { /// struct Sender1; /// impl SendXcm for Sender1 { /// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { -/// return Err(Error::CannotReachDestination(destination, message)) +/// return Err(Error::NotApplicable(destination, message)) /// } /// } /// @@ -215,7 +215,7 @@ impl ExecuteXcm for () { /// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { /// match destination { /// MultiLocation::X1(j) if j == Junction::Parent => Ok(()), -/// _ => Err(Error::CannotReachDestination(destination, message)), +/// _ => Err(Error::NotApplicable(destination, message)), /// } /// } /// } @@ -243,7 +243,7 @@ pub trait SendXcm { /// Send an XCM `message` to a given `destination`. /// /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST* - /// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without + /// return `NotApplicable`. Any other error will cause the tuple implementation to exit early without /// trying other type fields. fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result; } @@ -254,10 +254,10 @@ impl SendXcm for Tuple { for_tuples!( #( // we shadow `destination` and `message` in each expansion for the next one. let (destination, message) = match Tuple::send_xcm(destination, message) { - Err(Error::CannotReachDestination(d, m)) => (d, m), + Err(Error::NotApplicable(d, m)) => (d, m), o @ _ => return o, }; )* ); - Err(Error::CannotReachDestination(destination, message)) + Err(Error::NotApplicable(destination, message)) } } diff --git a/xcm/src/v1/traits.rs b/xcm/src/v1/traits.rs index 33b60455b0b2..c896ef38f8a5 100644 --- a/xcm/src/v1/traits.rs +++ b/xcm/src/v1/traits.rs @@ -42,7 +42,7 @@ pub enum Error { /// A human-readable explanation of the specific issue is provided. SendFailed(#[codec(skip)] &'static str), /// The message and destination combination was not recognized as being reachable. - CannotReachDestination(MultiLocation, Xcm<()>), + NotApplicable(MultiLocation, Xcm<()>), MultiLocationFull, FailedToDecode, BadOrigin, @@ -188,7 +188,7 @@ impl ExecuteXcm for () { /// Utility for sending an XCM message. /// /// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return -/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination` +/// `NotApplicable` to pass the execution to the next sender item. Note that each `NotApplicable` /// might alter the destination and the XCM message for to the next router. /// /// @@ -201,7 +201,7 @@ impl ExecuteXcm for () { /// struct Sender1; /// impl SendXcm for Sender1 { /// fn send_xcm(destination: impl Into, message: Xcm<()>) -> Result { -/// return Err(Error::CannotReachDestination(destination.into(), message)) +/// return Err(Error::NotApplicable(destination.into(), message)) /// } /// } /// @@ -230,7 +230,7 @@ impl ExecuteXcm for () { /// { /// Ok(()) /// } else { -/// Err(Error::CannotReachDestination(destination, message)) +/// Err(Error::NotApplicable(destination, message)) /// } /// } /// } @@ -257,7 +257,7 @@ pub trait SendXcm { /// Send an XCM `message` to a given `destination`. /// /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST* - /// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without + /// return `NotApplicable`. Any other error will cause the tuple implementation to exit early without /// trying other type fields. fn send_xcm(destination: impl Into, message: Xcm<()>) -> Result; } @@ -268,10 +268,10 @@ impl SendXcm for Tuple { for_tuples!( #( // we shadow `destination` and `message` in each expansion for the next one. let (destination, message) = match Tuple::send_xcm(destination, message) { - Err(Error::CannotReachDestination(d, m)) => (d, m), + Err(Error::NotApplicable(d, m)) => (d, m), o @ _ => return o, }; )* ); - Err(Error::CannotReachDestination(destination.into(), message)) + Err(Error::NotApplicable(destination.into(), message)) } } diff --git a/xcm/src/v2/traits.rs b/xcm/src/v2/traits.rs index 519ce89823c7..af0240c1ad26 100644 --- a/xcm/src/v2/traits.rs +++ b/xcm/src/v2/traits.rs @@ -146,7 +146,7 @@ impl TryFrom for Error { impl From for Error { fn from(e: SendError) -> Self { match e { - SendError::CannotReachDestination(..) | SendError::Unroutable => Error::Unroutable, + SendError::NotApplicable(..) | SendError::Unroutable => Error::Unroutable, SendError::Transport(s) => Error::Transport(s), SendError::DestinationUnsupported => Error::DestinationUnsupported, SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, @@ -243,7 +243,7 @@ pub enum SendError { /// /// This is not considered fatal: if there are alternative transport routes available, then /// they may be attempted. For this reason, the destination and message are contained. - CannotReachDestination(MultiLocation, Xcm<()>), + NotApplicable(MultiLocation, Xcm<()>), /// Destination is routable, but there is some issue with the transport mechanism. This is /// considered fatal. /// A human-readable explanation of the specific issue is provided. @@ -264,7 +264,7 @@ pub type SendResult = result::Result<(), SendError>; /// Utility for sending an XCM message. /// /// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return -/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination` +/// `NotApplicable` to pass the execution to the next sender item. Note that each `NotApplicable` /// might alter the destination and the XCM message for to the next router. /// /// @@ -277,7 +277,7 @@ pub type SendResult = result::Result<(), SendError>; /// struct Sender1; /// impl SendXcm for Sender1 { /// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// return Err(SendError::CannotReachDestination(destination.into(), message)) +/// return Err(SendError::NotApplicable(destination.into(), message)) /// } /// } /// @@ -300,7 +300,7 @@ pub type SendResult = result::Result<(), SendError>; /// let destination = destination.into(); /// match destination { /// MultiLocation { parents: 1, interior: Here } => Ok(()), -/// _ => Err(SendError::CannotReachDestination(destination, message)), +/// _ => Err(SendError::NotApplicable(destination, message)), /// } /// } /// } @@ -331,7 +331,7 @@ pub trait SendXcm { /// Send an XCM `message` to a given `destination`. /// /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST* - /// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without + /// return `NotApplicable`. Any other error will cause the tuple implementation to exit early without /// trying other type fields. fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult; } @@ -342,10 +342,10 @@ impl SendXcm for Tuple { for_tuples!( #( // we shadow `destination` and `message` in each expansion for the next one. let (destination, message) = match Tuple::send_xcm(destination, message) { - Err(SendError::CannotReachDestination(d, m)) => (d, m), + Err(SendError::NotApplicable(d, m)) => (d, m), o @ _ => return o, }; )* ); - Err(SendError::CannotReachDestination(destination.into(), message)) + Err(SendError::NotApplicable(destination.into(), message)) } } diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 052f2317f39a..36f2b6f53e3f 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -167,7 +167,7 @@ impl TryFrom for Error { impl From for Error { fn from(e: SendError) -> Self { match e { - SendError::CannotReachDestination | + SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument => Error::Unroutable, SendError::Transport(s) => Error::Transport(s), @@ -297,7 +297,7 @@ pub enum SendError { /// /// This is not considered fatal: if there are alternative transport routes available, then /// they may be attempted. - CannotReachDestination, + NotApplicable, /// Destination is routable, but there is some issue with the transport mechanism. This is /// considered fatal. /// A human-readable explanation of the specific issue is provided. @@ -340,7 +340,7 @@ impl Unwrappable for Option { /// Utility for sending an XCM message. /// /// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return -/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination` +/// `NotApplicable` to pass the execution to the next sender item. Note that each `NotApplicable` /// might alter the destination and the XCM message for to the next router. /// /// @@ -355,7 +355,7 @@ impl Unwrappable for Option { /// impl SendXcm for Sender1 { /// type OptionTicket = Option; /// fn validate(_: &mut Option, _: &mut Option>) -> SendResult { -/// Err(SendError::CannotReachDestination) +/// Err(SendError::NotApplicable) /// } /// fn deliver(_: Infallible) -> Result<(), SendError> { /// unreachable!() @@ -384,7 +384,7 @@ impl Unwrappable for Option { /// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { /// match destination.as_ref().ok_or(SendError::MissingArgument)? { /// MultiLocation { parents: 1, interior: Here } => Ok(((), MultiAssets::new())), -/// _ => Err(SendError::CannotReachDestination), +/// _ => Err(SendError::NotApplicable), /// } /// } /// fn deliver(_: ()) -> Result<(), SendError> { @@ -416,10 +416,10 @@ pub trait SendXcm { /// which can be used to enact delivery. /// /// The `destination` and `message` must be `Some` (or else an error will be returned) and they - /// may only be consumed if the `Err` is not `CannotReachDestination`. + /// may only be consumed if the `Err` is not `NotApplicable`. /// /// If it is not a destination which can be reached with this type but possibly could by others, - /// then this *MUST* return `CannotReachDestination`. Any other error will cause the tuple + /// then this *MUST* return `NotApplicable`. Any other error will cause the tuple /// implementation to exit early without trying other type fields. fn validate( destination: &mut Option, @@ -445,7 +445,7 @@ impl SendXcm for Tuple { ::none() } else { match Tuple::validate(destination, message) { - Err(SendError::CannotReachDestination) => ::none(), + Err(SendError::NotApplicable) => ::none(), Err(e) => { return Err(e) }, Ok((v, c)) => { maybe_cost = Some(c); @@ -457,7 +457,7 @@ impl SendXcm for Tuple { if let Some(cost) = maybe_cost { Ok((one_ticket, cost)) } else { - Err(SendError::CannotReachDestination) + Err(SendError::NotApplicable) } } diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index 8704485004de..674c1ca33e6d 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -115,7 +115,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S ) -> SendResult> { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { - return Err(CannotReachDestination) + return Err(NotApplicable) } let message = message.take().ok_or(MissingArgument)?; Ok((message, MultiAssets::new())) @@ -155,7 +155,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S ) -> SendResult> { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { - return Err(CannotReachDestination) + return Err(NotApplicable) } let message = message.take().ok_or(MissingArgument)?; Ok((message, MultiAssets::new())) diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index f09409d2e17a..8c36c6ed4c81 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -70,7 +70,7 @@ impl> SendXcm Ok(x) => x, Err(d) => { *dest = Some(d); - return Err(CannotReachDestination) + return Err(NotApplicable) }, }; let (network, destination, local_network, local_location) = devolved; @@ -157,7 +157,7 @@ impl xcm: &mut Option>, ) -> SendResult<::Inner> { let d = dest.as_ref().ok_or(MissingArgument)?.clone(); - let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| CannotReachDestination)?; + let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| NotApplicable)?; let (remote_network, remote_location, local_network, local_location) = devolved; // Prepend the desired message with instructions which effectively rewrite the origin. @@ -172,7 +172,7 @@ impl let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) - .ok_or(CannotReachDestination)?; + .ok_or(NotApplicable)?; ensure!(maybe_payment.is_none(), Unroutable); // We then send a normal message to the bridge asking it to export the prepended @@ -216,7 +216,7 @@ impl xcm: &mut Option>, ) -> SendResult<::Inner> { let d = dest.as_ref().ok_or(MissingArgument)?.clone(); - let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| CannotReachDestination)?; + let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| NotApplicable)?; let (remote_network, remote_location, local_network, local_location) = devolved; // Prepend the desired message with instructions which effectively rewrite the origin. @@ -231,7 +231,7 @@ impl let (bridge, maybe_payment) = Bridges::exporter_for(&remote_network, &remote_location, &exported) - .ok_or(CannotReachDestination)?; + .ok_or(NotApplicable)?; let local_from_bridge = MultiLocation::from(Ancestry::get()).inverted(&bridge).map_err(|_| Unroutable)?; @@ -340,14 +340,14 @@ impl, Price: Get> message: &mut Option>, ) -> Result<(Vec, MultiAssets), SendError> { let bridged_network = BridgedNetwork::get(); - ensure!(&network == &bridged_network, SendError::CannotReachDestination); + ensure!(&network == &bridged_network, SendError::NotApplicable); // We don't/can't use the `channel` for this adapter. let dest = destination.take().ok_or(SendError::MissingArgument)?; let universal_dest = match dest.pushed_front_with(GlobalConsensus(bridged_network)) { Ok(d) => d.into(), Err((dest, _)) => { *destination = Some(dest); - return Err(SendError::CannotReachDestination) + return Err(SendError::NotApplicable) }, }; let message = VersionedXcm::from(message.take().ok_or(SendError::MissingArgument)?); diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index fe17d5d50c1e..661af6cf9313 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -24,10 +24,10 @@ pub trait ExportXcm { /// which can be used to enact delivery. /// /// The `destination` and `message` must be `Some` (or else an error will be returned) and they - /// may only be consumed if the `Err` is not `CannotReachDestination`. + /// may only be consumed if the `Err` is not `NotApplicable`. /// /// If it is not a destination which can be reached with this type but possibly could by others, - /// then this *MUST* return `CannotReachDestination`. Any other error will cause the tuple + /// then this *MUST* return `NotApplicable`. Any other error will cause the tuple /// implementation to exit early without trying other type fields. fn validate( network: NetworkId, @@ -60,7 +60,7 @@ impl ExportXcm for Tuple { ::none() } else { match Tuple::validate(network, channel, destination, message) { - Err(SendError::CannotReachDestination) => ::none(), + Err(SendError::NotApplicable) => ::none(), Err(e) => { return Err(e) }, Ok((v, c)) => { maybe_cost = Some(c); @@ -72,7 +72,7 @@ impl ExportXcm for Tuple { if let Some(cost) = maybe_cost { Ok((one_ticket, cost)) } else { - Err(SendError::CannotReachDestination) + Err(SendError::NotApplicable) } } diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index b04daff4a171..9b9dc5482279 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -311,7 +311,7 @@ macro_rules! decl_test_network { )* _ => { *destination = Some(d); - return Err($crate::SendError::CannotReachDestination) + return Err($crate::SendError::NotApplicable) }, } let m = message.take().ok_or($crate::SendError::MissingArgument)?; @@ -342,7 +342,7 @@ macro_rules! decl_test_network { )* _ => { *destination = Some(d); - return Err($crate::SendError::CannotReachDestination) + return Err($crate::SendError::NotApplicable) }, } let m = message.take().ok_or($crate::SendError::MissingArgument)?; From 18f11009abf0a36577c53f629c1e6009d7396492 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 7 Feb 2022 19:22:22 +0000 Subject: [PATCH 46/57] Remove XCM v0 --- xcm/procedural/src/lib.rs | 8 - xcm/procedural/src/v0.rs | 17 - xcm/procedural/src/v0/multilocation.rs | 115 ---- xcm/procedural/src/v1/multilocation.rs | 57 -- xcm/src/lib.rs | 136 +---- xcm/src/v0/junction.rs | 220 -------- xcm/src/v0/mod.rs | 388 -------------- xcm/src/v0/multi_asset.rs | 410 -------------- xcm/src/v0/multi_location.rs | 715 ------------------------- xcm/src/v0/order.rs | 207 ------- xcm/src/v0/traits.rs | 263 --------- xcm/src/v1/junction.rs | 22 +- xcm/src/v1/mod.rs | 187 ++++--- xcm/src/v1/multiasset.rs | 94 ---- xcm/src/v1/multilocation.rs | 22 - xcm/src/v1/order.rs | 54 +- 16 files changed, 134 insertions(+), 2781 deletions(-) delete mode 100644 xcm/procedural/src/v0.rs delete mode 100644 xcm/procedural/src/v0/multilocation.rs delete mode 100644 xcm/src/v0/junction.rs delete mode 100644 xcm/src/v0/mod.rs delete mode 100644 xcm/src/v0/multi_asset.rs delete mode 100644 xcm/src/v0/multi_location.rs delete mode 100644 xcm/src/v0/order.rs delete mode 100644 xcm/src/v0/traits.rs diff --git a/xcm/procedural/src/lib.rs b/xcm/procedural/src/lib.rs index 5339247a1597..d16476c4f047 100644 --- a/xcm/procedural/src/lib.rs +++ b/xcm/procedural/src/lib.rs @@ -18,18 +18,10 @@ use proc_macro::TokenStream; -mod v0; mod v1; mod v3; mod weight_info; -#[proc_macro] -pub fn impl_conversion_functions_for_multilocation_v0(input: TokenStream) -> TokenStream { - v0::multilocation::generate_conversion_functions(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - #[proc_macro] pub fn impl_conversion_functions_for_multilocation_v1(input: TokenStream) -> TokenStream { v1::multilocation::generate_conversion_functions(input) diff --git a/xcm/procedural/src/v0.rs b/xcm/procedural/src/v0.rs deleted file mode 100644 index 7774df4e9f8f..000000000000 --- a/xcm/procedural/src/v0.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -pub mod multilocation; diff --git a/xcm/procedural/src/v0/multilocation.rs b/xcm/procedural/src/v0/multilocation.rs deleted file mode 100644 index 247ef856b14e..000000000000 --- a/xcm/procedural/src/v0/multilocation.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote}; - -pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> syn::Result { - if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")) - } - - let from_tuples = generate_conversion_from_tuples(); - let from_v1 = generate_conversion_from_v1(); - - Ok(quote! { - #from_tuples - #from_v1 - }) -} - -fn generate_conversion_from_tuples() -> TokenStream { - let from_tuples = (0..8usize) - .map(|num_junctions| { - let junctions = - (0..=num_junctions).map(|_| format_ident!("Junction")).collect::>(); - let idents = (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); - let variant = &format_ident!("X{}", num_junctions + 1); - let array_size = num_junctions + 1; - - quote! { - impl From<( #(#junctions,)* )> for MultiLocation { - fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self { - MultiLocation::#variant( #(#idents),* ) - } - } - - impl From<[Junction; #array_size]> for MultiLocation { - fn from(j: [Junction; #array_size]) -> Self { - let [#(#idents),*] = j; - MultiLocation::#variant( #(#idents),* ) - } - } - } - }) - .collect::(); - - quote! { - impl From<()> for MultiLocation { - fn from(_: ()) -> Self { - MultiLocation::Null - } - } - - impl From for MultiLocation { - fn from(x: Junction) -> Self { - MultiLocation::X1(x) - } - } - - impl From<[Junction; 0]> for MultiLocation { - fn from(_: [Junction; 0]) -> Self { - MultiLocation::Null - } - } - - #from_tuples - } -} - -fn generate_conversion_from_v1() -> TokenStream { - let match_variants = (0..8u8) - .map(|cur_num| { - let variant = format_ident!("X{}", cur_num + 1); - let idents = (1..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); - - quote! { - crate::v1::Junctions::#variant( j0 #(, #idents)* ) => res - .pushed_with(Junction::from(j0)) - #( .and_then(|res| res.pushed_with(Junction::from(#idents))) )* - .map_err(|_| ()), - } - }) - .collect::(); - - quote! { - impl core::convert::TryFrom for MultiLocation { - type Error = (); - fn try_from(v1: crate::v1::MultiLocation) -> core::result::Result { - let mut res = MultiLocation::Null; - - for _ in 0..v1.parents { - res.push(Junction::Parent)?; - } - - match v1.interior { - crate::v1::Junctions::Here => Ok(res), - #match_variants - } - } - } - } -} diff --git a/xcm/procedural/src/v1/multilocation.rs b/xcm/procedural/src/v1/multilocation.rs index 2fe7dbee8c1f..15a0d322a204 100644 --- a/xcm/procedural/src/v1/multilocation.rs +++ b/xcm/procedural/src/v1/multilocation.rs @@ -25,12 +25,10 @@ pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result TokenStream { } } -fn generate_conversion_from_v0() -> TokenStream { - let match_variants = (0..8u8) - .map(|cur_num| { - let num_ancestors = cur_num + 1; - let variant = format_ident!("X{}", num_ancestors); - let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); - - let intermediate_match_arms = (1..num_ancestors) - .rev() - .map(|parent_count| { - let parent_idents = - (0..parent_count).map(|j| format_ident!("j{}", j)).collect::>(); - let junction_idents = (parent_count..num_ancestors) - .map(|j| format_ident!("j{}", j)) - .collect::>(); - let junction_variant = format_ident!("X{}", num_ancestors - parent_count); - - quote! { - crate::v0::MultiLocation::#variant( #(#idents),* ) - if #( #parent_idents.is_parent() )&&* => - Ok(MultiLocation { - parents: #parent_count, - interior: #junction_variant( #( core::convert::TryInto::try_into(#junction_idents)? ),* ), - }), - } - }) - .collect::(); - - quote! { - crate::v0::MultiLocation::#variant( #(#idents),* ) - if #( #idents.is_parent() )&&* => - Ok(MultiLocation::ancestor(#num_ancestors)), - #intermediate_match_arms - crate::v0::MultiLocation::#variant( #(#idents),* ) => - Ok( #variant( #( core::convert::TryInto::try_into(#idents)? ),* ).into() ), - } - }) - .collect::(); - - quote! { - impl core::convert::TryFrom for MultiLocation { - type Error = (); - fn try_from(mut v0: crate::v0::MultiLocation) -> core::result::Result { - use Junctions::*; - - v0.canonicalize(); - match v0 { - crate::v0::MultiLocation::Null => Ok(Here.into()), - #match_variants - } - } - } - } -} - fn generate_conversion_from_v3() -> TokenStream { let match_variants = (0..8u8) .map(|cur_num| { diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index aa48165bf73e..b8b5ea98c2f4 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -23,7 +23,6 @@ #![no_std] extern crate alloc; -use alloc::vec::Vec; use core::{ convert::{TryFrom, TryInto}, result::Result, @@ -32,7 +31,6 @@ use derivative::Derivative; use parity_scale_codec::{Decode, Encode, Error as CodecError, Input}; use scale_info::TypeInfo; -pub mod v0; pub mod v1; pub mod v2; pub mod v3; @@ -76,15 +74,15 @@ pub trait IntoVersion: Sized { #[codec(encode_bound())] #[codec(decode_bound())] pub enum VersionedMultiLocation { - V0(v0::MultiLocation), + #[codec(index = 1)] V1(v1::MultiLocation), + #[codec(index = 2)] V3(v3::MultiLocation), } impl IntoVersion for VersionedMultiLocation { fn into_version(self, n: Version) -> Result { Ok(match n { - 0 => Self::V0(self.try_into()?), 1 | 2 => Self::V1(self.try_into()?), 3 => Self::V3(self.try_into()?), _ => return Err(()), @@ -92,12 +90,6 @@ impl IntoVersion for VersionedMultiLocation { } } -impl From for VersionedMultiLocation { - fn from(x: v0::MultiLocation) -> Self { - VersionedMultiLocation::V0(x) - } -} - impl From for VersionedMultiLocation { fn from(x: v1::MultiLocation) -> Self { VersionedMultiLocation::V1(x) @@ -110,24 +102,11 @@ impl> From for VersionedMultiLocation { } } -impl TryFrom for v0::MultiLocation { - type Error = (); - fn try_from(x: VersionedMultiLocation) -> Result { - use VersionedMultiLocation::*; - match x { - V0(x) => Ok(x), - V1(x) => x.try_into(), - V3(x) => V1(x.try_into()?).try_into(), - } - } -} - impl TryFrom for v1::MultiLocation { type Error = (); fn try_from(x: VersionedMultiLocation) -> Result { use VersionedMultiLocation::*; match x { - V0(x) => x.try_into(), V1(x) => Ok(x), V3(x) => x.try_into(), } @@ -139,7 +118,6 @@ impl TryFrom for v3::MultiLocation { fn try_from(x: VersionedMultiLocation) -> Result { use VersionedMultiLocation::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => x.try_into(), V3(x) => Ok(x), } @@ -152,7 +130,9 @@ impl TryFrom for v3::MultiLocation { #[codec(encode_bound())] #[codec(decode_bound())] pub enum VersionedInteriorMultiLocation { + #[codec(index = 0)] V1(v1::InteriorMultiLocation), + #[codec(index = 1)] V3(v3::InteriorMultiLocation), } @@ -206,16 +186,17 @@ impl TryFrom for v3::InteriorMultiLocation { #[codec(encode_bound())] #[codec(decode_bound())] pub enum VersionedResponse { - V0(v0::Response), + #[codec(index = 1)] V1(v1::Response), + #[codec(index = 2)] V2(v2::Response), + #[codec(index = 3)] V3(v3::Response), } impl IntoVersion for VersionedResponse { fn into_version(self, n: Version) -> Result { Ok(match n { - 0 => Self::V0(self.try_into()?), 1 => Self::V1(self.try_into()?), 2 => Self::V2(self.try_into()?), 3 => Self::V3(self.try_into()?), @@ -224,12 +205,6 @@ impl IntoVersion for VersionedResponse { } } -impl From for VersionedResponse { - fn from(x: v0::Response) -> Self { - VersionedResponse::V0(x) - } -} - impl From for VersionedResponse { fn from(x: v1::Response) -> Self { VersionedResponse::V1(x) @@ -248,25 +223,11 @@ impl> From for VersionedResponse { } } -impl TryFrom for v0::Response { - type Error = (); - fn try_from(x: VersionedResponse) -> Result { - use VersionedResponse::*; - match x { - V0(x) => Ok(x), - V1(x) => x.try_into(), - V2(x) => V1(x.try_into()?).try_into(), - V3(x) => V2(x.try_into()?).try_into(), - } - } -} - impl TryFrom for v1::Response { type Error = (); fn try_from(x: VersionedResponse) -> Result { use VersionedResponse::*; match x { - V0(x) => x.try_into(), V1(x) => Ok(x), V2(x) => x.try_into(), V3(x) => V2(x.try_into()?).try_into(), @@ -279,7 +240,6 @@ impl TryFrom for v2::Response { fn try_from(x: VersionedResponse) -> Result { use VersionedResponse::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => x.try_into(), V2(x) => Ok(x), V3(x) => x.try_into(), @@ -292,7 +252,6 @@ impl TryFrom for v3::Response { fn try_from(x: VersionedResponse) -> Result { use VersionedResponse::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => V2(x.try_into()?).try_into(), V2(x) => x.try_into(), V3(x) => Ok(x), @@ -306,15 +265,15 @@ impl TryFrom for v3::Response { #[codec(encode_bound())] #[codec(decode_bound())] pub enum VersionedMultiAsset { - V0(v0::MultiAsset), + #[codec(index = 1)] V1(v1::MultiAsset), + #[codec(index = 2)] V3(v3::MultiAsset), } impl IntoVersion for VersionedMultiAsset { fn into_version(self, n: Version) -> Result { Ok(match n { - 0 => Self::V0(self.try_into()?), 1 | 2 => Self::V1(self.try_into()?), 3 => Self::V3(self.try_into()?), _ => return Err(()), @@ -322,12 +281,6 @@ impl IntoVersion for VersionedMultiAsset { } } -impl From for VersionedMultiAsset { - fn from(x: v0::MultiAsset) -> Self { - VersionedMultiAsset::V0(x) - } -} - impl From for VersionedMultiAsset { fn from(x: v1::MultiAsset) -> Self { VersionedMultiAsset::V1(x) @@ -340,24 +293,11 @@ impl From for VersionedMultiAsset { } } -impl TryFrom for v0::MultiAsset { - type Error = (); - fn try_from(x: VersionedMultiAsset) -> Result { - use VersionedMultiAsset::*; - match x { - V0(x) => Ok(x), - V1(x) => x.try_into(), - V3(x) => V1(x.try_into()?).try_into(), - } - } -} - impl TryFrom for v1::MultiAsset { type Error = (); fn try_from(x: VersionedMultiAsset) -> Result { use VersionedMultiAsset::*; match x { - V0(x) => x.try_into(), V1(x) => Ok(x), V3(x) => x.try_into(), } @@ -369,7 +309,6 @@ impl TryFrom for v3::MultiAsset { fn try_from(x: VersionedMultiAsset) -> Result { use VersionedMultiAsset::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => x.try_into(), V3(x) => Ok(x), } @@ -382,15 +321,15 @@ impl TryFrom for v3::MultiAsset { #[codec(encode_bound())] #[codec(decode_bound())] pub enum VersionedMultiAssets { - V0(Vec), + #[codec(index = 1)] V1(v1::MultiAssets), + #[codec(index = 2)] V3(v3::MultiAssets), } impl IntoVersion for VersionedMultiAssets { fn into_version(self, n: Version) -> Result { Ok(match n { - 0 => Self::V0(self.try_into()?), 1 | 2 => Self::V1(self.try_into()?), 3 => Self::V3(self.try_into()?), _ => return Err(()), @@ -398,12 +337,6 @@ impl IntoVersion for VersionedMultiAssets { } } -impl From> for VersionedMultiAssets { - fn from(x: Vec) -> Self { - VersionedMultiAssets::V0(x) - } -} - impl From for VersionedMultiAssets { fn from(x: v1::MultiAssets) -> Self { VersionedMultiAssets::V1(x) @@ -416,24 +349,11 @@ impl> From for VersionedMultiAssets { } } -impl TryFrom for Vec { - type Error = (); - fn try_from(x: VersionedMultiAssets) -> Result { - use VersionedMultiAssets::*; - match x { - V0(x) => Ok(x), - V1(x) => x.try_into(), - V3(x) => V1(x.try_into()?).try_into(), - } - } -} - impl TryFrom for v1::MultiAssets { type Error = (); fn try_from(x: VersionedMultiAssets) -> Result { use VersionedMultiAssets::*; match x { - V0(x) => x.try_into(), V1(x) => Ok(x), V3(x) => x.try_into(), } @@ -445,7 +365,6 @@ impl TryFrom for v3::MultiAssets { fn try_from(x: VersionedMultiAssets) -> Result { use VersionedMultiAssets::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => x.try_into(), V3(x) => Ok(x), } @@ -459,16 +378,17 @@ impl TryFrom for v3::MultiAssets { #[codec(decode_bound())] #[scale_info(bounds(), skip_type_params(Call))] pub enum VersionedXcm { - V0(v0::Xcm), + #[codec(index = 1)] V1(v1::Xcm), + #[codec(index = 2)] V2(v2::Xcm), + #[codec(index = 3)] V3(v3::Xcm), } impl IntoVersion for VersionedXcm { fn into_version(self, n: Version) -> Result { Ok(match n { - 0 => Self::V0(self.try_into()?), 1 => Self::V1(self.try_into()?), 2 => Self::V2(self.try_into()?), 3 => Self::V3(self.try_into()?), @@ -477,12 +397,6 @@ impl IntoVersion for VersionedXcm { } } -impl From> for VersionedXcm { - fn from(x: v0::Xcm) -> Self { - VersionedXcm::V0(x) - } -} - impl From> for VersionedXcm { fn from(x: v1::Xcm) -> Self { VersionedXcm::V1(x) @@ -501,25 +415,11 @@ impl From> for VersionedXcm { } } -impl TryFrom> for v0::Xcm { - type Error = (); - fn try_from(x: VersionedXcm) -> Result { - use VersionedXcm::*; - match x { - V0(x) => Ok(x), - V1(x) => x.try_into(), - V2(x) => V1(x.try_into()?).try_into(), - V3(x) => V2(x.try_into()?).try_into(), - } - } -} - impl TryFrom> for v1::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { - V0(x) => x.try_into(), V1(x) => Ok(x), V2(x) => x.try_into(), V3(x) => V2(x.try_into()?).try_into(), @@ -532,7 +432,6 @@ impl TryFrom> for v2::Xcm { fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => x.try_into(), V2(x) => Ok(x), V3(x) => x.try_into(), @@ -545,7 +444,6 @@ impl TryFrom> for v3::Xcm { fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { - V0(x) => V1(x.try_into()?).try_into(), V1(x) => V2(x.try_into()?).try_into(), V2(x) => x.try_into(), V3(x) => Ok(x), @@ -608,12 +506,6 @@ pub mod prelude { } pub mod opaque { - pub mod v0 { - // Everything from v0 - pub use crate::v0::*; - // Then override with the opaque types in v0 - pub use crate::v0::opaque::{Order, Xcm}; - } pub mod v1 { // Everything from v1 pub use crate::v1::*; diff --git a/xcm/src/v0/junction.rs b/xcm/src/v0/junction.rs deleted file mode 100644 index 7ab3f5b1ad9f..000000000000 --- a/xcm/src/v0/junction.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Support data structures for `MultiLocation`, primarily the `Junction` datatype. - -use crate::v3::NetworkId as NewNetworkId; -use alloc::vec::Vec; -use core::convert::TryInto; -use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; - -/// A global identifier of an account-bearing consensus system. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] -pub enum NetworkId { - /// Unidentified/any. - Any, - /// Some named network. - Named(Vec), - /// The Polkadot Relay chain - Polkadot, - /// Kusama. - Kusama, -} - -impl TryInto for Option { - type Error = (); - fn try_into(self) -> Result { - use NewNetworkId::*; - Ok(match self { - None => NetworkId::Any, - Some(Polkadot) => NetworkId::Polkadot, - Some(Kusama) => NetworkId::Kusama, - _ => return Err(()), - }) - } -} - -/// An identifier of a pluralistic body. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] -pub enum BodyId { - /// The only body in its context. - Unit, - /// A named body. - Named(Vec), - /// An indexed body. - Index(#[codec(compact)] u32), - /// The unambiguous executive body (for Polkadot, this would be the Polkadot council). - Executive, - /// The unambiguous technical body (for Polkadot, this would be the Technical Committee). - Technical, - /// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of - /// lock-voters). - Legislative, - /// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it - /// may be considered as that). - Judicial, -} - -/// A part of a pluralistic body. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] -pub enum BodyPart { - /// The body's declaration, under whatever means it decides. - Voice, - /// A given number of members of the body. - Members { - #[codec(compact)] - count: u32, - }, - /// A given number of members of the body, out of some larger caucus. - Fraction { - #[codec(compact)] - nom: u32, - #[codec(compact)] - denom: u32, - }, - /// No less than the given proportion of members of the body. - AtLeastProportion { - #[codec(compact)] - nom: u32, - #[codec(compact)] - denom: u32, - }, - /// More than than the given proportion of members of the body. - MoreThanProportion { - #[codec(compact)] - nom: u32, - #[codec(compact)] - denom: u32, - }, -} - -impl BodyPart { - /// Returns `true` if the part represents a strict majority (> 50%) of the body in question. - pub fn is_majority(&self) -> bool { - match self { - BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true, - BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true, - BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true, - _ => false, - } - } -} - -/// A single item in a path to describe the relative location of a consensus system. -/// -/// Each item assumes a pre-existing location as its context and is defined in terms of it. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] -pub enum Junction { - /// The consensus system of which the context is a member and state-wise super-set. - /// - /// NOTE: This item is *not* a sub-consensus item: a consensus system may not identify itself trustlessly as - /// a location that includes this junction. - Parent, - /// An indexed parachain belonging to and operated by the context. - /// - /// Generally used when the context is a Polkadot Relay-chain. - Parachain(#[codec(compact)] u32), - /// A 32-byte identifier for an account of a specific network that is respected as a sovereign endpoint within - /// the context. - /// - /// Generally used when the context is a Substrate-based chain. - AccountId32 { network: NetworkId, id: [u8; 32] }, - /// An 8-byte index for an account of a specific network that is respected as a sovereign endpoint within - /// the context. - /// - /// May be used when the context is a Frame-based chain and includes e.g. an indices pallet. - AccountIndex64 { - network: NetworkId, - #[codec(compact)] - index: u64, - }, - /// A 20-byte identifier for an account of a specific network that is respected as a sovereign endpoint within - /// the context. - /// - /// May be used when the context is an Ethereum or Bitcoin chain or smart-contract. - AccountKey20 { network: NetworkId, key: [u8; 20] }, - /// An instanced, indexed pallet that forms a constituent part of the context. - /// - /// Generally used when the context is a Frame-based chain. - PalletInstance(u8), - /// A non-descript index within the context location. - /// - /// Usage will vary widely owing to its generality. - /// - /// NOTE: Try to avoid using this and instead use a more specific item. - GeneralIndex(#[codec(compact)] u128), - /// A nondescript datum acting as a key within the context location. - /// - /// Usage will vary widely owing to its generality. - /// - /// NOTE: Try to avoid using this and instead use a more specific item. - GeneralKey(Vec), - /// The unambiguous child. - /// - /// Not currently used except as a fallback when deriving ancestry. - OnlyChild, - /// A pluralistic body existing within consensus. - /// - /// Typical to be used to represent a governance origin of a chain, but could in principle be used to represent - /// things such as multisigs also. - Plurality { id: BodyId, part: BodyPart }, -} - -impl From for Junction { - fn from(v1: crate::v1::Junction) -> Junction { - use crate::v1::Junction::*; - match v1 { - Parachain(id) => Self::Parachain(id), - AccountId32 { network, id } => Self::AccountId32 { network, id }, - AccountIndex64 { network, index } => Self::AccountIndex64 { network, index }, - AccountKey20 { network, key } => Self::AccountKey20 { network, key }, - PalletInstance(index) => Self::PalletInstance(index), - GeneralIndex(index) => Self::GeneralIndex(index), - GeneralKey(key) => Self::GeneralKey(key), - OnlyChild => Self::OnlyChild, - Plurality { id, part } => Self::Plurality { id, part }, - } - } -} - -impl Junction { - /// Returns true if this junction is a `Parent` item. - pub fn is_parent(&self) -> bool { - match self { - Junction::Parent => true, - _ => false, - } - } - - /// Returns true if this junction can be considered an interior part of its context. This is generally `true`, - /// except for the `Parent` item. - pub fn is_interior(&self) -> bool { - match self { - Junction::Parent => false, - - Junction::Parachain(..) | - Junction::AccountId32 { .. } | - Junction::AccountIndex64 { .. } | - Junction::AccountKey20 { .. } | - Junction::PalletInstance { .. } | - Junction::GeneralIndex { .. } | - Junction::GeneralKey(..) | - Junction::OnlyChild | - Junction::Plurality { .. } => true, - } - } -} diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs deleted file mode 100644 index 35889d8daab2..000000000000 --- a/xcm/src/v0/mod.rs +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Version 0 of the Cross-Consensus Message format data structures. - -use crate::DoubleEncoded; -use alloc::vec::Vec; -use core::{ - convert::{TryFrom, TryInto}, - result, -}; -use derivative::Derivative; -use parity_scale_codec::{self, Decode, Encode}; -use scale_info::TypeInfo; - -mod junction; -mod multi_asset; -mod multi_location; -mod order; -mod traits; -use super::v1::{MultiLocation as MultiLocation1, Response as Response1, Xcm as Xcm1}; -pub use junction::{BodyId, BodyPart, Junction, NetworkId}; -pub use multi_asset::{AssetInstance, MultiAsset}; -pub use multi_location::MultiLocation::{self, *}; -pub use order::Order; -pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm}; - -/// A prelude for importing all types typically used when interacting with XCM messages. -pub mod prelude { - pub use super::{ - junction::{BodyId, Junction::*}, - multi_asset::{ - AssetInstance::{self, *}, - MultiAsset::{self, *}, - }, - multi_location::MultiLocation::{self, *}, - order::Order::{self, *}, - traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm}, - Junction::*, - OriginKind, - Xcm::{self, *}, - }; -} - -// TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec, using initial byte values 128+ to encode -// the number of items in the vector. - -/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`. -#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] -pub enum OriginKind { - /// Origin should just be the native dispatch origin representation for the sender in the - /// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin - /// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a - /// primary/native dispatch origin form. - Native, - - /// Origin should just be the standard account-based origin with the sovereign account of - /// the sender. For Cumulus/Frame chains, this is the `Signed` origin. - SovereignAccount, - - /// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin. - /// This will not usually be an available option. - Superuser, - - /// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be - /// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be - /// the `pallet_xcm::Origin::Xcm` type. - Xcm, -} - -/// Response data to a query. -#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] -pub enum Response { - /// Some assets. - Assets(Vec), -} - -/// Cross-Consensus Message: A message from one consensus system to another. -/// -/// Consensus systems that may send and receive messages include blockchains and smart contracts. -/// -/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`. -/// -/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer -/// XCM format, known as `VersionedXcm`. -#[derive(Derivative, Encode, Decode, TypeInfo)] -#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] -#[codec(encode_bound())] -#[codec(decode_bound())] -#[scale_info(bounds(), skip_type_params(Call))] -pub enum Xcm { - /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the - /// orders (`effects`). - /// - /// - `assets`: The asset(s) to be withdrawn into holding. - /// - `effects`: The order(s) to execute on the holding account. - /// - /// Kind: *Instruction*. - /// - /// Errors: - #[codec(index = 0)] - WithdrawAsset { assets: Vec, effects: Vec> }, - - /// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system. - /// - /// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have - /// been placed into `holding`. - /// - /// - `assets`: The asset(s) that are minted into holding. - /// - `effects`: The order(s) to execute on the holding account. - /// - /// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be - /// withdrawn should this system send a corresponding message. - /// - /// Kind: *Trusted Indication*. - /// - /// Errors: - #[codec(index = 1)] - ReserveAssetDeposit { assets: Vec, effects: Vec> }, - - /// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be - /// created on this system. - /// - /// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have - /// been placed into `holding`. - /// - /// - `assets`: The asset(s) that are minted into holding. - /// - `effects`: The order(s) to execute on the holding account. - /// - /// Safety: `origin` must be trusted to have irrevocably destroyed the `assets` prior as a consequence of - /// sending this message. - /// - /// Kind: *Trusted Indication*. - /// - /// Errors: - #[codec(index = 2)] - TeleportAsset { assets: Vec, effects: Vec> }, - - /// Indication of the contents of the holding account corresponding to the `QueryHolding` order of `query_id`. - /// - /// - `query_id`: The identifier of the query that resulted in this message being sent. - /// - `assets`: The message content. - /// - /// Safety: No concerns. - /// - /// Kind: *Information*. - /// - /// Errors: - #[codec(index = 3)] - QueryResponse { - #[codec(compact)] - query_id: u64, - response: Response, - }, - - /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the - /// ownership of `dest` within this consensus system. - /// - /// - `assets`: The asset(s) to be withdrawn. - /// - `dest`: The new owner for the assets. - /// - /// Safety: No concerns. - /// - /// Kind: *Instruction*. - /// - /// Errors: - #[codec(index = 4)] - TransferAsset { assets: Vec, dest: MultiLocation }, - - /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the - /// ownership of `dest` within this consensus system. - /// - /// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`. - /// - /// - `assets`: The asset(s) to be withdrawn. - /// - `dest`: The new owner for the assets. - /// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to - /// `dest`. - /// - /// Safety: No concerns. - /// - /// Kind: *Instruction*. - /// - /// Errors: - #[codec(index = 5)] - TransferReserveAsset { assets: Vec, dest: MultiLocation, effects: Vec> }, - - /// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind - /// of origin `origin_type`. - /// - /// - `origin_type`: The means of expressing the message origin as a dispatch origin. - /// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will - /// be used in the weight determination arithmetic. - /// - `call`: The encoded transaction to be applied. - /// - /// Safety: No concerns. - /// - /// Kind: *Instruction*. - /// - /// Errors: - #[codec(index = 6)] - Transact { origin_type: OriginKind, require_weight_at_most: u64, call: DoubleEncoded }, - - /// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the - /// relay-chain to a para. - /// - /// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening. - /// - `max_message_size`: The maximum size of a message proposed by the sender. - /// - `max_capacity`: The maximum number of messages that can be queued in the channel. - /// - /// Safety: The message should originate directly from the relay-chain. - /// - /// Kind: *System Notification* - #[codec(index = 7)] - HrmpNewChannelOpenRequest { - #[codec(compact)] - sender: u32, - #[codec(compact)] - max_message_size: u32, - #[codec(compact)] - max_capacity: u32, - }, - - /// A message to notify about that a previously sent open channel request has been accepted by - /// the recipient. That means that the channel will be opened during the next relay-chain session - /// change. This message is meant to be sent by the relay-chain to a para. - /// - /// Safety: The message should originate directly from the relay-chain. - /// - /// Kind: *System Notification* - /// - /// Errors: - #[codec(index = 8)] - HrmpChannelAccepted { - #[codec(compact)] - recipient: u32, - }, - - /// A message to notify that the other party in an open channel decided to close it. In particular, - /// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close - /// will be enacted at the next relay-chain session change. This message is meant to be sent by - /// the relay-chain to a para. - /// - /// Safety: The message should originate directly from the relay-chain. - /// - /// Kind: *System Notification* - /// - /// Errors: - #[codec(index = 9)] - HrmpChannelClosing { - #[codec(compact)] - initiator: u32, - #[codec(compact)] - sender: u32, - #[codec(compact)] - recipient: u32, - }, - - /// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus - /// location within the origin. - /// - /// Safety: `who` must be an interior location of the context. This basically means that no `Parent` - /// junctions are allowed in it. This should be verified at the time of XCM execution. - /// - /// Kind: *Instruction* - /// - /// Errors: - #[codec(index = 10)] - RelayedFrom { who: MultiLocation, message: alloc::boxed::Box> }, -} - -impl Xcm { - pub fn into(self) -> Xcm { - Xcm::from(self) - } - pub fn from(xcm: Xcm) -> Self { - use Xcm::*; - match xcm { - WithdrawAsset { assets, effects } => - WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - ReserveAssetDeposit { assets, effects } => ReserveAssetDeposit { - assets, - effects: effects.into_iter().map(Order::into).collect(), - }, - TeleportAsset { assets, effects } => - TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - QueryResponse { query_id, response } => QueryResponse { query_id, response }, - TransferAsset { assets, dest } => TransferAsset { assets, dest }, - TransferReserveAsset { assets, dest, effects } => - TransferReserveAsset { assets, dest, effects }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, - HrmpChannelClosing { initiator, sender, recipient } => - HrmpChannelClosing { initiator, sender, recipient }, - Transact { origin_type, require_weight_at_most, call } => - Transact { origin_type, require_weight_at_most, call: call.into() }, - RelayedFrom { who, message } => - RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) }, - } - } -} - -pub mod opaque { - /// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a - /// call other than it is pre-encoded. - pub type Xcm = super::Xcm<()>; - - pub use super::order::opaque::*; -} - -// Convert from a v1 response to a v0 response -impl TryFrom for Response { - type Error = (); - fn try_from(new_response: Response1) -> result::Result { - Ok(match new_response { - Response1::Assets(assets) => Self::Assets(assets.try_into()?), - Response1::Version(..) => return Err(()), - }) - } -} - -impl TryFrom> for Xcm { - type Error = (); - fn try_from(x: Xcm1) -> result::Result, ()> { - use Xcm::*; - Ok(match x { - Xcm1::WithdrawAsset { assets, effects } => WithdrawAsset { - assets: assets.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - Xcm1::ReserveAssetDeposited { assets, effects } => ReserveAssetDeposit { - assets: assets.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - Xcm1::ReceiveTeleportedAsset { assets, effects } => TeleportAsset { - assets: assets.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - Xcm1::QueryResponse { query_id, response } => - QueryResponse { query_id, response: response.try_into()? }, - Xcm1::TransferAsset { assets, beneficiary } => - TransferAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? }, - Xcm1::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { - assets: assets.try_into()?, - dest: dest.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - Xcm1::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - Xcm1::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, - Xcm1::HrmpChannelClosing { initiator, sender, recipient } => - HrmpChannelClosing { initiator, sender, recipient }, - Xcm1::Transact { origin_type, require_weight_at_most, call } => - Transact { origin_type, require_weight_at_most, call: call.into() }, - Xcm1::RelayedFrom { who, message } => RelayedFrom { - who: MultiLocation1 { interior: who, parents: 0 }.try_into()?, - message: alloc::boxed::Box::new((*message).try_into()?), - }, - Xcm1::SubscribeVersion { .. } | Xcm1::UnsubscribeVersion => return Err(()), - }) - } -} diff --git a/xcm/src/v0/multi_asset.rs b/xcm/src/v0/multi_asset.rs deleted file mode 100644 index dac5a6bfeb7e..000000000000 --- a/xcm/src/v0/multi_asset.rs +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Cross-Consensus Message format data structures. - -use super::MultiLocation; -use crate::v1::{MultiAssetFilter, MultiAssets, WildMultiAsset}; -use alloc::{vec, vec::Vec}; -use core::{ - convert::{TryFrom, TryInto}, - result, -}; -use parity_scale_codec::{self, Decode, Encode}; -use scale_info::TypeInfo; - -pub use crate::v1::AssetInstance; - -/// A single general identifier for an asset. -/// -/// Represents both fungible and non-fungible assets. May only be used to represent a single asset class. -/// -/// Wildcards may or may not be allowed by the interpreting context. -/// -/// Assets classes may be identified in one of two ways: either an abstract identifier or a concrete identifier. -/// Implementations may support only one of these. A single asset may be referenced from multiple asset identifiers, -/// though will tend to have only a single *preferred* identifier. -/// -/// ### Abstract identifiers -/// -/// Abstract identifiers are absolute identifiers that represent a notional asset which can exist within multiple -/// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay of -/// the consensus system in which it is interpreted. -/// -/// However, in the attempt to provide uniformity across consensus systems, they may conflate different instantiations -/// of some notional asset (e.g. the reserve asset and a local reserve-backed derivative of it) under the same name, -/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may not -/// be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none being -/// fungible between the others. -/// -/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions do not -/// occur. -/// -/// An abstract identifier is represented as a simple variable-size byte string. As of writing, no global registry -/// exists and no proposals have been put forth for asset labeling. -/// -/// ### Concrete identifiers -/// -/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in a -/// consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non -/// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind of -/// central registry. -/// -/// The limitation is that the asset identifier cannot be trivially copied between consensus systems and must instead be -/// "re-anchored" whenever being moved to a new consensus system, using the two systems' relative paths. -/// -/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will have -/// the desired meaning/effect. This means that relative paths should always by constructed to be read from the point of -/// view of the receiving system, *which may be have a completely different meaning in the authoring system*. -/// -/// Concrete identifiers are the preferred way of identifying an asset since they are entirely unambiguous. -/// -/// A concrete identifier is represented by a `MultiLocation`. If a system has an unambiguous primary asset (such as -/// Bitcoin with BTC or Ethereum with ETH), then it will conventionally be identified as the chain itself. Alternative -/// and more specific ways of referring to an asset within a system include: -/// -/// - `/PalletInstance()` for a Frame chain with a single-asset pallet instance (such as an instance of the -/// Balances pallet). -/// - `/PalletInstance()/GeneralIndex()` for a Frame chain with an indexed multi-asset pallet instance -/// (such as an instance of the Assets pallet). -/// - `/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain. -/// - `/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain. -/// -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] -pub enum MultiAsset { - /// No assets. Rarely used. - None, - - /// All assets. Typically used for the subset of assets to be used for an `Order`, and in that context means - /// "all assets currently in holding". - All, - - /// All fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that context - /// means "all fungible assets currently in holding". - AllFungible, - - /// All non-fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that - /// context means "all non-fungible assets currently in holding". - AllNonFungible, - - /// All fungible assets of a given abstract asset `id`entifier. - AllAbstractFungible { id: Vec }, - - /// All non-fungible assets of a given abstract asset `class`. - AllAbstractNonFungible { class: Vec }, - - /// All fungible assets of a given concrete asset `id`entifier. - AllConcreteFungible { id: MultiLocation }, - - /// All non-fungible assets of a given concrete asset `class`. - AllConcreteNonFungible { class: MultiLocation }, - - /// Some specific `amount` of the fungible asset identified by an abstract `id`. - AbstractFungible { - id: Vec, - #[codec(compact)] - amount: u128, - }, - - /// Some specific `instance` of the non-fungible asset whose `class` is identified abstractly. - AbstractNonFungible { class: Vec, instance: AssetInstance }, - - /// Some specific `amount` of the fungible asset identified by an concrete `id`. - ConcreteFungible { - id: MultiLocation, - #[codec(compact)] - amount: u128, - }, - - /// Some specific `instance` of the non-fungible asset whose `class` is identified concretely. - ConcreteNonFungible { class: MultiLocation, instance: AssetInstance }, -} - -impl MultiAsset { - /// Returns `true` if the `MultiAsset` is a wildcard and can refer to classes of assets, instead of just one. - /// - /// Typically can also be inferred by the name starting with `All`. - pub fn is_wildcard(&self) -> bool { - match self { - MultiAsset::None | - MultiAsset::AbstractFungible { .. } | - MultiAsset::AbstractNonFungible { .. } | - MultiAsset::ConcreteFungible { .. } | - MultiAsset::ConcreteNonFungible { .. } => false, - - MultiAsset::All | - MultiAsset::AllFungible | - MultiAsset::AllNonFungible | - MultiAsset::AllAbstractFungible { .. } | - MultiAsset::AllConcreteFungible { .. } | - MultiAsset::AllAbstractNonFungible { .. } | - MultiAsset::AllConcreteNonFungible { .. } => true, - } - } - - fn is_none(&self) -> bool { - match self { - MultiAsset::None | - MultiAsset::AbstractFungible { amount: 0, .. } | - MultiAsset::ConcreteFungible { amount: 0, .. } => true, - - _ => false, - } - } - - fn is_fungible(&self) -> bool { - match self { - MultiAsset::All | - MultiAsset::AllFungible | - MultiAsset::AllAbstractFungible { .. } | - MultiAsset::AllConcreteFungible { .. } | - MultiAsset::AbstractFungible { .. } | - MultiAsset::ConcreteFungible { .. } => true, - - _ => false, - } - } - - fn is_non_fungible(&self) -> bool { - match self { - MultiAsset::All | - MultiAsset::AllNonFungible | - MultiAsset::AllAbstractNonFungible { .. } | - MultiAsset::AllConcreteNonFungible { .. } | - MultiAsset::AbstractNonFungible { .. } | - MultiAsset::ConcreteNonFungible { .. } => true, - - _ => false, - } - } - - fn is_concrete_fungible(&self, id: &MultiLocation) -> bool { - match self { - MultiAsset::AllFungible => true, - MultiAsset::AllConcreteFungible { id: i } | - MultiAsset::ConcreteFungible { id: i, .. } => i == id, - - _ => false, - } - } - - fn is_abstract_fungible(&self, id: &[u8]) -> bool { - match self { - MultiAsset::AllFungible => true, - MultiAsset::AllAbstractFungible { id: i } | - MultiAsset::AbstractFungible { id: i, .. } => i == id, - _ => false, - } - } - - fn is_concrete_non_fungible(&self, class: &MultiLocation) -> bool { - match self { - MultiAsset::AllNonFungible => true, - MultiAsset::AllConcreteNonFungible { class: i } | - MultiAsset::ConcreteNonFungible { class: i, .. } => i == class, - _ => false, - } - } - - fn is_abstract_non_fungible(&self, class: &[u8]) -> bool { - match self { - MultiAsset::AllNonFungible => true, - MultiAsset::AllAbstractNonFungible { class: i } | - MultiAsset::AbstractNonFungible { class: i, .. } => i == class, - _ => false, - } - } - - fn is_all(&self) -> bool { - matches!(self, MultiAsset::All) - } - - /// Returns true if `self` is a super-set of the given `inner`. - /// - /// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard. - /// For more details, see the implementation and tests. - pub fn contains(&self, inner: &MultiAsset) -> bool { - use MultiAsset::*; - - // Inner cannot be wild - if inner.is_wildcard() { - return false - } - // Everything contains nothing. - if inner.is_none() { - return true - } - - // Everything contains anything. - if self.is_all() { - return true - } - // Nothing contains nothing. - if self.is_none() { - return false - } - - match self { - // Anything fungible contains "all fungibles" - AllFungible => inner.is_fungible(), - // Anything non-fungible contains "all non-fungibles" - AllNonFungible => inner.is_non_fungible(), - - AllConcreteFungible { id } => inner.is_concrete_fungible(id), - AllAbstractFungible { id } => inner.is_abstract_fungible(id), - AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class), - AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class), - - ConcreteFungible { id, amount } => matches!( - inner, - ConcreteFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount - ), - AbstractFungible { id, amount } => matches!( - inner, - AbstractFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount - ), - ConcreteNonFungible { .. } => self == inner, - AbstractNonFungible { .. } => self == inner, - _ => false, - } - } - - pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - use MultiAsset::*; - match self { - AllConcreteFungible { ref mut id } | - AllConcreteNonFungible { class: ref mut id } | - ConcreteFungible { ref mut id, .. } | - ConcreteNonFungible { class: ref mut id, .. } => - id.prepend_with(prepend.clone()).map_err(|_| ()), - _ => Ok(()), - } - } -} - -impl TryFrom for MultiAsset { - type Error = (); - - fn try_from(m: crate::v1::MultiAsset) -> result::Result { - use crate::v1::{AssetId::*, Fungibility::*}; - use MultiAsset::*; - Ok(match (m.id, m.fun) { - (Concrete(id), Fungible(amount)) => ConcreteFungible { id: id.try_into()?, amount }, - (Concrete(class), NonFungible(instance)) => - ConcreteNonFungible { class: class.try_into()?, instance }, - (Abstract(id), Fungible(amount)) => AbstractFungible { id, amount }, - (Abstract(class), NonFungible(instance)) => AbstractNonFungible { class, instance }, - }) - } -} - -impl TryFrom for Vec { - type Error = (); - - fn try_from(m: MultiAssets) -> result::Result, ()> { - m.drain().into_iter().map(MultiAsset::try_from).collect() - } -} - -impl TryFrom for MultiAsset { - type Error = (); - - fn try_from(m: WildMultiAsset) -> result::Result { - use crate::v1::{AssetId::*, WildFungibility::*}; - use MultiAsset::*; - Ok(match m { - WildMultiAsset::All => All, - WildMultiAsset::AllOf { id, fun } => match (id, fun) { - (Concrete(id), Fungible) => AllConcreteFungible { id: id.try_into()? }, - (Concrete(class), NonFungible) => - AllConcreteNonFungible { class: class.try_into()? }, - (Abstract(id), Fungible) => AllAbstractFungible { id }, - (Abstract(class), NonFungible) => AllAbstractNonFungible { class }, - }, - }) - } -} - -impl TryFrom for Vec { - type Error = (); - - fn try_from(m: WildMultiAsset) -> result::Result, ()> { - Ok(vec![m.try_into()?]) - } -} - -impl TryFrom for Vec { - type Error = (); - - fn try_from(m: MultiAssetFilter) -> result::Result, ()> { - match m { - MultiAssetFilter::Definite(assets) => assets.try_into(), - MultiAssetFilter::Wild(wildcard) => wildcard.try_into(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn contains_works() { - use alloc::vec; - use MultiAsset::*; - // trivial case: all contains any non-wildcard. - assert!(All.contains(&None)); - assert!(All.contains(&AbstractFungible { id: alloc::vec![99u8], amount: 1 })); - - // trivial case: none contains nothing, except itself. - assert!(None.contains(&None)); - assert!(!None.contains(&AllFungible)); - assert!(!None.contains(&All)); - - // A bit more sneaky: Nothing can contain wildcard, even All ir the thing itself. - assert!(!All.contains(&All)); - assert!(!All.contains(&AllFungible)); - assert!(!AllFungible.contains(&AllFungible)); - assert!(!AllNonFungible.contains(&AllNonFungible)); - - // For fungibles, containing is basically equality, or equal id with higher amount. - assert!(!AbstractFungible { id: vec![99u8], amount: 99 } - .contains(&AbstractFungible { id: vec![1u8], amount: 99 })); - assert!(AbstractFungible { id: vec![99u8], amount: 99 } - .contains(&AbstractFungible { id: vec![99u8], amount: 99 })); - assert!(AbstractFungible { id: vec![99u8], amount: 99 } - .contains(&AbstractFungible { id: vec![99u8], amount: 9 })); - assert!(!AbstractFungible { id: vec![99u8], amount: 99 } - .contains(&AbstractFungible { id: vec![99u8], amount: 100 })); - - // For non-fungibles, containing is equality. - assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) } - .contains(&AbstractNonFungible { - class: vec![98u8], - instance: AssetInstance::Index(9) - })); - assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(8) } - .contains(&AbstractNonFungible { - class: vec![99u8], - instance: AssetInstance::Index(9) - })); - assert!(AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) } - .contains(&AbstractNonFungible { - class: vec![99u8], - instance: AssetInstance::Index(9) - })); - } -} diff --git a/xcm/src/v0/multi_location.rs b/xcm/src/v0/multi_location.rs deleted file mode 100644 index 6b7438b93fa0..000000000000 --- a/xcm/src/v0/multi_location.rs +++ /dev/null @@ -1,715 +0,0 @@ -// Copyright 2020-2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Cross-Consensus Message format data structures. - -use super::Junction; -use core::{mem, result}; -use parity_scale_codec::{self, Decode, Encode}; - -/// A relative path between state-bearing consensus systems. -/// -/// A location in a consensus system is defined as an *isolatable state machine* held within global consensus. The -/// location in question need not have a sophisticated consensus algorithm of its own; a single account within -/// Ethereum, for example, could be considered a location. -/// -/// A very-much non-exhaustive list of types of location include: -/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain. -/// - A layer-0 super-chain, e.g. the Polkadot Relay chain. -/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum. -/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based Substrate chain. -/// - An account. -/// -/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the relative path -/// between two locations, and cannot generally be used to refer to a location universally. It is comprised of a -/// number of *junctions*, each morphing the previous location, either diving down into one of its internal locations, -/// called a *sub-consensus*, or going up into its parent location. Correct `MultiLocation` values must have all -/// `Parent` junctions as a prefix to all *sub-consensus* junctions. -/// -/// This specific `MultiLocation` implementation uses a Rust `enum` in order to make pattern matching easier. -/// -/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, scale_info::TypeInfo)] -pub enum MultiLocation { - /// The interpreting consensus system. - Null, - /// A relative path comprising 1 junction. - X1(Junction), - /// A relative path comprising 2 junctions. - X2(Junction, Junction), - /// A relative path comprising 3 junctions. - X3(Junction, Junction, Junction), - /// A relative path comprising 4 junctions. - X4(Junction, Junction, Junction, Junction), - /// A relative path comprising 5 junctions. - X5(Junction, Junction, Junction, Junction, Junction), - /// A relative path comprising 6 junctions. - X6(Junction, Junction, Junction, Junction, Junction, Junction), - /// A relative path comprising 7 junctions. - X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction), - /// A relative path comprising 8 junctions. - X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction), -} - -/// Maximum number of junctions a `MultiLocation` can contain. -pub const MAX_MULTILOCATION_LENGTH: usize = 8; - -xcm_procedural::impl_conversion_functions_for_multilocation_v0!(); - -pub struct MultiLocationIterator(MultiLocation); -impl Iterator for MultiLocationIterator { - type Item = Junction; - fn next(&mut self) -> Option { - self.0.take_first() - } -} - -pub struct MultiLocationReverseIterator(MultiLocation); -impl Iterator for MultiLocationReverseIterator { - type Item = Junction; - fn next(&mut self) -> Option { - self.0.take_last() - } -} - -pub struct MultiLocationRefIterator<'a>(&'a MultiLocation, usize); -impl<'a> Iterator for MultiLocationRefIterator<'a> { - type Item = &'a Junction; - fn next(&mut self) -> Option<&'a Junction> { - let result = self.0.at(self.1); - self.1 += 1; - result - } -} - -pub struct MultiLocationReverseRefIterator<'a>(&'a MultiLocation, usize); -impl<'a> Iterator for MultiLocationReverseRefIterator<'a> { - type Item = &'a Junction; - fn next(&mut self) -> Option<&'a Junction> { - self.1 += 1; - self.0.at(self.0.len().checked_sub(self.1)?) - } -} - -impl MultiLocation { - /// Returns first junction, or `None` if the location is empty. - pub fn first(&self) -> Option<&Junction> { - match &self { - MultiLocation::Null => None, - MultiLocation::X1(ref a) => Some(a), - MultiLocation::X2(ref a, ..) => Some(a), - MultiLocation::X3(ref a, ..) => Some(a), - MultiLocation::X4(ref a, ..) => Some(a), - MultiLocation::X5(ref a, ..) => Some(a), - MultiLocation::X6(ref a, ..) => Some(a), - MultiLocation::X7(ref a, ..) => Some(a), - MultiLocation::X8(ref a, ..) => Some(a), - } - } - - /// Returns last junction, or `None` if the location is empty. - pub fn last(&self) -> Option<&Junction> { - match &self { - MultiLocation::Null => None, - MultiLocation::X1(ref a) => Some(a), - MultiLocation::X2(.., ref a) => Some(a), - MultiLocation::X3(.., ref a) => Some(a), - MultiLocation::X4(.., ref a) => Some(a), - MultiLocation::X5(.., ref a) => Some(a), - MultiLocation::X6(.., ref a) => Some(a), - MultiLocation::X7(.., ref a) => Some(a), - MultiLocation::X8(.., ref a) => Some(a), - } - } - - /// Splits off the first junction, returning the remaining suffix (first item in tuple) and the first element - /// (second item in tuple) or `None` if it was empty. - pub fn split_first(self) -> (MultiLocation, Option) { - match self { - MultiLocation::Null => (MultiLocation::Null, None), - MultiLocation::X1(a) => (MultiLocation::Null, Some(a)), - MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)), - MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)), - MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(b, c, d), Some(a)), - MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(b, c, d, e), Some(a)), - MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(b, c, d, e, f), Some(a)), - MultiLocation::X7(a, b, c, d, e, f, g) => - (MultiLocation::X6(b, c, d, e, f, g), Some(a)), - MultiLocation::X8(a, b, c, d, e, f, g, h) => - (MultiLocation::X7(b, c, d, e, f, g, h), Some(a)), - } - } - - /// Splits off the last junction, returning the remaining prefix (first item in tuple) and the last element - /// (second item in tuple) or `None` if it was empty. - pub fn split_last(self) -> (MultiLocation, Option) { - match self { - MultiLocation::Null => (MultiLocation::Null, None), - MultiLocation::X1(a) => (MultiLocation::Null, Some(a)), - MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)), - MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)), - MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(a, b, c), Some(d)), - MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(a, b, c, d), Some(e)), - MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(a, b, c, d, e), Some(f)), - MultiLocation::X7(a, b, c, d, e, f, g) => - (MultiLocation::X6(a, b, c, d, e, f), Some(g)), - MultiLocation::X8(a, b, c, d, e, f, g, h) => - (MultiLocation::X7(a, b, c, d, e, f, g), Some(h)), - } - } - - /// Removes the first element from `self`, returning it (or `None` if it was empty). - pub fn take_first(&mut self) -> Option { - let mut d = MultiLocation::Null; - mem::swap(&mut *self, &mut d); - let (tail, head) = d.split_first(); - *self = tail; - head - } - - /// Removes the last element from `self`, returning it (or `None` if it was empty). - pub fn take_last(&mut self) -> Option { - let mut d = MultiLocation::Null; - mem::swap(&mut *self, &mut d); - let (head, tail) = d.split_last(); - *self = head; - tail - } - - /// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with the original value of - /// `self` in case of overflow. - pub fn pushed_with(self, new: Junction) -> result::Result { - Ok(match self { - MultiLocation::Null => MultiLocation::X1(new), - MultiLocation::X1(a) => MultiLocation::X2(a, new), - MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new), - MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new), - MultiLocation::X4(a, b, c, d) => MultiLocation::X5(a, b, c, d, new), - MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(a, b, c, d, e, new), - MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(a, b, c, d, e, f, new), - MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(a, b, c, d, e, f, g, new), - s => Err(s)?, - }) - } - - /// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the original value of - /// `self` in case of overflow. - pub fn pushed_front_with(self, new: Junction) -> result::Result { - Ok(match self { - MultiLocation::Null => MultiLocation::X1(new), - MultiLocation::X1(a) => MultiLocation::X2(new, a), - MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b), - MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c), - MultiLocation::X4(a, b, c, d) => MultiLocation::X5(new, a, b, c, d), - MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(new, a, b, c, d, e), - MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(new, a, b, c, d, e, f), - MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(new, a, b, c, d, e, f, g), - s => Err(s)?, - }) - } - - /// Returns the number of junctions in `self`. - pub fn len(&self) -> usize { - match &self { - MultiLocation::Null => 0, - MultiLocation::X1(..) => 1, - MultiLocation::X2(..) => 2, - MultiLocation::X3(..) => 3, - MultiLocation::X4(..) => 4, - MultiLocation::X5(..) => 5, - MultiLocation::X6(..) => 6, - MultiLocation::X7(..) => 7, - MultiLocation::X8(..) => 8, - } - } - - /// Returns the junction at index `i`, or `None` if the location doesn't contain that many elements. - pub fn at(&self, i: usize) -> Option<&Junction> { - Some(match (i, &self) { - (0, MultiLocation::X1(ref a)) => a, - (0, MultiLocation::X2(ref a, ..)) => a, - (0, MultiLocation::X3(ref a, ..)) => a, - (0, MultiLocation::X4(ref a, ..)) => a, - (0, MultiLocation::X5(ref a, ..)) => a, - (0, MultiLocation::X6(ref a, ..)) => a, - (0, MultiLocation::X7(ref a, ..)) => a, - (0, MultiLocation::X8(ref a, ..)) => a, - (1, MultiLocation::X2(_, ref a)) => a, - (1, MultiLocation::X3(_, ref a, ..)) => a, - (1, MultiLocation::X4(_, ref a, ..)) => a, - (1, MultiLocation::X5(_, ref a, ..)) => a, - (1, MultiLocation::X6(_, ref a, ..)) => a, - (1, MultiLocation::X7(_, ref a, ..)) => a, - (1, MultiLocation::X8(_, ref a, ..)) => a, - (2, MultiLocation::X3(_, _, ref a)) => a, - (2, MultiLocation::X4(_, _, ref a, ..)) => a, - (2, MultiLocation::X5(_, _, ref a, ..)) => a, - (2, MultiLocation::X6(_, _, ref a, ..)) => a, - (2, MultiLocation::X7(_, _, ref a, ..)) => a, - (2, MultiLocation::X8(_, _, ref a, ..)) => a, - (3, MultiLocation::X4(_, _, _, ref a)) => a, - (3, MultiLocation::X5(_, _, _, ref a, ..)) => a, - (3, MultiLocation::X6(_, _, _, ref a, ..)) => a, - (3, MultiLocation::X7(_, _, _, ref a, ..)) => a, - (3, MultiLocation::X8(_, _, _, ref a, ..)) => a, - (4, MultiLocation::X5(_, _, _, _, ref a)) => a, - (4, MultiLocation::X6(_, _, _, _, ref a, ..)) => a, - (4, MultiLocation::X7(_, _, _, _, ref a, ..)) => a, - (4, MultiLocation::X8(_, _, _, _, ref a, ..)) => a, - (5, MultiLocation::X6(_, _, _, _, _, ref a)) => a, - (5, MultiLocation::X7(_, _, _, _, _, ref a, ..)) => a, - (5, MultiLocation::X8(_, _, _, _, _, ref a, ..)) => a, - (6, MultiLocation::X7(_, _, _, _, _, _, ref a)) => a, - (6, MultiLocation::X8(_, _, _, _, _, _, ref a, ..)) => a, - (7, MultiLocation::X8(_, _, _, _, _, _, _, ref a)) => a, - _ => return None, - }) - } - - /// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't contain that many - /// elements. - pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { - Some(match (i, self) { - (0, MultiLocation::X1(ref mut a)) => a, - (0, MultiLocation::X2(ref mut a, ..)) => a, - (0, MultiLocation::X3(ref mut a, ..)) => a, - (0, MultiLocation::X4(ref mut a, ..)) => a, - (0, MultiLocation::X5(ref mut a, ..)) => a, - (0, MultiLocation::X6(ref mut a, ..)) => a, - (0, MultiLocation::X7(ref mut a, ..)) => a, - (0, MultiLocation::X8(ref mut a, ..)) => a, - (1, MultiLocation::X2(_, ref mut a)) => a, - (1, MultiLocation::X3(_, ref mut a, ..)) => a, - (1, MultiLocation::X4(_, ref mut a, ..)) => a, - (1, MultiLocation::X5(_, ref mut a, ..)) => a, - (1, MultiLocation::X6(_, ref mut a, ..)) => a, - (1, MultiLocation::X7(_, ref mut a, ..)) => a, - (1, MultiLocation::X8(_, ref mut a, ..)) => a, - (2, MultiLocation::X3(_, _, ref mut a)) => a, - (2, MultiLocation::X4(_, _, ref mut a, ..)) => a, - (2, MultiLocation::X5(_, _, ref mut a, ..)) => a, - (2, MultiLocation::X6(_, _, ref mut a, ..)) => a, - (2, MultiLocation::X7(_, _, ref mut a, ..)) => a, - (2, MultiLocation::X8(_, _, ref mut a, ..)) => a, - (3, MultiLocation::X4(_, _, _, ref mut a)) => a, - (3, MultiLocation::X5(_, _, _, ref mut a, ..)) => a, - (3, MultiLocation::X6(_, _, _, ref mut a, ..)) => a, - (3, MultiLocation::X7(_, _, _, ref mut a, ..)) => a, - (3, MultiLocation::X8(_, _, _, ref mut a, ..)) => a, - (4, MultiLocation::X5(_, _, _, _, ref mut a)) => a, - (4, MultiLocation::X6(_, _, _, _, ref mut a, ..)) => a, - (4, MultiLocation::X7(_, _, _, _, ref mut a, ..)) => a, - (4, MultiLocation::X8(_, _, _, _, ref mut a, ..)) => a, - (5, MultiLocation::X6(_, _, _, _, _, ref mut a)) => a, - (5, MultiLocation::X7(_, _, _, _, _, ref mut a, ..)) => a, - (5, MultiLocation::X8(_, _, _, _, _, ref mut a, ..)) => a, - (6, MultiLocation::X7(_, _, _, _, _, _, ref mut a)) => a, - (6, MultiLocation::X8(_, _, _, _, _, _, ref mut a, ..)) => a, - (7, MultiLocation::X8(_, _, _, _, _, _, _, ref mut a)) => a, - _ => return None, - }) - } - - /// Returns a reference iterator over the junctions. - pub fn iter(&self) -> MultiLocationRefIterator { - MultiLocationRefIterator(&self, 0) - } - - /// Returns a reference iterator over the junctions in reverse. - pub fn iter_rev(&self) -> MultiLocationReverseRefIterator { - MultiLocationReverseRefIterator(&self, 0) - } - - /// Consumes `self` and returns an iterator over the junctions. - pub fn into_iter(self) -> MultiLocationIterator { - MultiLocationIterator(self) - } - - /// Consumes `self` and returns an iterator over the junctions in reverse. - pub fn into_iter_rev(self) -> MultiLocationReverseIterator { - MultiLocationReverseIterator(self) - } - - /// Ensures that self begins with `prefix` and that it has a single `Junction` item following. - /// If so, returns a reference to this `Junction` item. - /// - /// # Example - /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*}; - /// # fn main() { - /// let mut m = X3(Parent, PalletInstance(3), OnlyChild); - /// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild)); - /// assert_eq!(m.match_and_split(&X1(Parent)), None); - /// # } - /// ``` - pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> { - if prefix.len() + 1 != self.len() { - return None - } - for i in 0..prefix.len() { - if prefix.at(i) != self.at(i) { - return None - } - } - return self.at(prefix.len()) - } - - /// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow. - pub fn push(&mut self, new: Junction) -> result::Result<(), ()> { - let mut n = MultiLocation::Null; - mem::swap(&mut *self, &mut n); - match n.pushed_with(new) { - Ok(result) => { - *self = result; - Ok(()) - }, - Err(old) => { - *self = old; - Err(()) - }, - } - } - - /// Mutates `self`, prefixing it with `new`. Returns `Err` in case of overflow. - pub fn push_front(&mut self, new: Junction) -> result::Result<(), ()> { - let mut n = MultiLocation::Null; - mem::swap(&mut *self, &mut n); - match n.pushed_front_with(new) { - Ok(result) => { - *self = result; - Ok(()) - }, - Err(old) => { - *self = old; - Err(()) - }, - } - } - - /// Returns the number of `Parent` junctions at the beginning of `self`. - pub fn leading_parent_count(&self) -> usize { - use Junction::Parent; - match self { - MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 8, - - MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, ..) => 7, - MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 7, - - MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6, - MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6, - MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, Parent) => 6, - - MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, ..) => 5, - MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, ..) => 5, - MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, ..) => 5, - MultiLocation::X5(Parent, Parent, Parent, Parent, Parent) => 5, - - MultiLocation::X8(Parent, Parent, Parent, Parent, ..) => 4, - MultiLocation::X7(Parent, Parent, Parent, Parent, ..) => 4, - MultiLocation::X6(Parent, Parent, Parent, Parent, ..) => 4, - MultiLocation::X5(Parent, Parent, Parent, Parent, ..) => 4, - MultiLocation::X4(Parent, Parent, Parent, Parent) => 4, - - MultiLocation::X8(Parent, Parent, Parent, ..) => 3, - MultiLocation::X7(Parent, Parent, Parent, ..) => 3, - MultiLocation::X6(Parent, Parent, Parent, ..) => 3, - MultiLocation::X5(Parent, Parent, Parent, ..) => 3, - MultiLocation::X4(Parent, Parent, Parent, ..) => 3, - MultiLocation::X3(Parent, Parent, Parent) => 3, - - MultiLocation::X8(Parent, Parent, ..) => 2, - MultiLocation::X7(Parent, Parent, ..) => 2, - MultiLocation::X6(Parent, Parent, ..) => 2, - MultiLocation::X5(Parent, Parent, ..) => 2, - MultiLocation::X4(Parent, Parent, ..) => 2, - MultiLocation::X3(Parent, Parent, ..) => 2, - MultiLocation::X2(Parent, Parent) => 2, - - MultiLocation::X8(Parent, ..) => 1, - MultiLocation::X7(Parent, ..) => 1, - MultiLocation::X6(Parent, ..) => 1, - MultiLocation::X5(Parent, ..) => 1, - MultiLocation::X4(Parent, ..) => 1, - MultiLocation::X3(Parent, ..) => 1, - MultiLocation::X2(Parent, ..) => 1, - MultiLocation::X1(Parent) => 1, - _ => 0, - } - } - - /// This function ensures a multi-junction is in its canonicalized/normalized form, removing - /// any internal `[Non-Parent, Parent]` combinations. - pub fn canonicalize(&mut self) { - let mut normalized = MultiLocation::Null; - let mut iter = self.iter(); - // We build up the the new normalized path by taking items from the original multi-location. - // When the next item we would add is `Parent`, we instead remove the last item assuming - // it is non-parent. - const EXPECT_MESSAGE: &'static str = - "`self` is a well formed multi-location with N junctions; \ - this loop iterates over the junctions of `self`; \ - the loop can push to the new multi-location at most one time; \ - thus the size of the new multi-location is at most N junctions; \ - qed"; - while let Some(j) = iter.next() { - if j == &Junction::Parent { - match normalized.last() { - None | Some(Junction::Parent) => {}, - Some(_) => { - normalized.take_last(); - continue - }, - } - } - - normalized.push(j.clone()).expect(EXPECT_MESSAGE); - } - - core::mem::swap(self, &mut normalized); - } - - /// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned, - /// removing any internal `[Non-Parent, Parent]` combinations. - /// - /// In the case of overflow, `self` is unmodified and we return `Err` with `suffix`. - /// - /// # Example - /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*}; - /// # fn main() { - /// let mut m = X3(Parent, Parachain(21), OnlyChild); - /// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(())); - /// assert_eq!(m, X3(Parent, Parachain(21), PalletInstance(3))); - /// # } - /// ``` - pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> { - let mut prefix = suffix; - core::mem::swap(self, &mut prefix); - match self.prepend_with(prefix) { - Ok(()) => Ok(()), - Err(prefix) => { - let mut suffix = prefix; - core::mem::swap(self, &mut suffix); - Err(suffix) - }, - } - } - - /// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned, - /// removing any internal [Non-Parent, `Parent`] combinations. - /// - /// In the case of overflow, `self` is unmodified and we return `Err` with `prefix`. - /// - /// # Example - /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any}; - /// # fn main() { - /// let mut m = X3(Parent, Parent, PalletInstance(3)); - /// assert_eq!(m.prepend_with(X3(Parent, Parachain(21), OnlyChild)), Ok(())); - /// assert_eq!(m, X2(Parent, PalletInstance(3))); - /// # } - /// ``` - pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> { - let mut prefix = prefix; - - // This will guarantee that all `Parent` junctions in the prefix are leading, which is - // important for calculating the `skipped` items below. - prefix.canonicalize(); - - let self_leading_parents = self.leading_parent_count(); - // These are the number of `non-parent` items in the prefix that we can - // potentially remove if the original location leads with parents. - let prefix_rest = prefix.len() - prefix.leading_parent_count(); - // 2 * skipped items will be removed when performing the normalization below. - let skipped = self_leading_parents.min(prefix_rest); - - // Pre-pending this prefix would create a multi-location with too many junctions. - if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH { - return Err(prefix) - } - - // Here we cancel out `[Non-Parent, Parent]` items (normalization), where - // the non-parent item comes from the end of the prefix, and the parent item - // comes from the front of the original location. - // - // We calculated already how many of these there should be above. - for _ in 0..skipped { - let _non_parent = prefix.take_last(); - let _parent = self.take_first(); - debug_assert!( - _non_parent.is_some() && _non_parent != Some(Junction::Parent), - "prepend_with should always remove a non-parent from the end of the prefix", - ); - debug_assert!( - _parent == Some(Junction::Parent), - "prepend_with should always remove a parent from the front of the location", - ); - } - - for j in prefix.into_iter_rev() { - self.push_front(j) - .expect("len + prefix minus 2*skipped is less than max length; qed"); - } - Ok(()) - } - - /// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s - /// for which `Junction::is_interior` returns `false`. This is generally true, except for the - /// `Parent` item. - /// - /// # Example - /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any}; - /// # fn main() { - /// let parent = X1(Parent); - /// assert_eq!(parent.is_interior(), false); - /// let m = X2(PalletInstance(12), AccountIndex64 { network: Any, index: 23 }); - /// assert_eq!(m.is_interior(), true); - /// # } - /// ``` - pub fn is_interior(&self) -> bool { - self.iter().all(Junction::is_interior) - } -} - -#[cfg(test)] -mod tests { - use super::MultiLocation::{self, *}; - use crate::opaque::v0::{Junction::*, NetworkId::Any}; - - #[test] - fn match_and_split_works() { - let m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }); - assert_eq!(m.match_and_split(&X1(Parent)), None); - assert_eq!( - m.match_and_split(&X2(Parent, Parachain(42))), - Some(&AccountIndex64 { network: Any, index: 23 }) - ); - assert_eq!(m.match_and_split(&m), None); - } - - #[test] - fn append_with_works() { - let acc = AccountIndex64 { network: Any, index: 23 }; - let mut m = X2(Parent, Parachain(42)); - assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(())); - assert_eq!(m, X4(Parent, Parachain(42), PalletInstance(3), acc.clone())); - - // cannot append to create overly long multilocation - let acc = AccountIndex64 { network: Any, index: 23 }; - let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42)); - let suffix = X2(PalletInstance(3), acc.clone()); - assert_eq!(m.append_with(suffix.clone()), Err(suffix)); - } - - #[test] - fn prepend_with_works() { - let mut m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }); - assert_eq!(m.prepend_with(X2(Parent, OnlyChild)), Ok(())); - assert_eq!(m, X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 })); - - // cannot prepend to create overly long multilocation - let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42)); - let prefix = X2(Parent, Parent); - assert_eq!(m.prepend_with(prefix.clone()), Err(prefix)); - - // Can handle shared prefix and resizing correctly. - let mut m = X1(Parent); - let prefix = X8( - Parachain(100), - OnlyChild, - OnlyChild, - OnlyChild, - OnlyChild, - OnlyChild, - OnlyChild, - Parent, - ); - assert_eq!(m.prepend_with(prefix.clone()), Ok(())); - assert_eq!(m, X5(Parachain(100), OnlyChild, OnlyChild, OnlyChild, OnlyChild)); - - let mut m = X1(Parent); - let prefix = X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent); - assert_eq!(m.prepend_with(prefix.clone()), Err(prefix)); - - let mut m = X1(Parent); - let prefix = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent); - assert_eq!(m.prepend_with(prefix.clone()), Ok(())); - assert_eq!(m, X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent)); - - let mut m = X1(Parent); - let prefix = X8(Parent, Parent, Parent, Parent, OnlyChild, Parent, Parent, Parent); - assert_eq!(m.prepend_with(prefix.clone()), Ok(())); - assert_eq!(m, X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent)); - } - - #[test] - fn canonicalize_works() { - let mut m = X1(Parent); - m.canonicalize(); - assert_eq!(m, X1(Parent)); - - let mut m = X1(Parachain(1)); - m.canonicalize(); - assert_eq!(m, X1(Parachain(1))); - - let mut m = X6(Parent, Parachain(1), Parent, Parachain(2), Parent, Parachain(3)); - m.canonicalize(); - assert_eq!(m, X2(Parent, Parachain(3))); - - let mut m = X5(Parachain(1), Parent, Parachain(2), Parent, Parachain(3)); - m.canonicalize(); - assert_eq!(m, X1(Parachain(3))); - - let mut m = X6(Parachain(1), Parent, Parachain(2), Parent, Parachain(3), Parent); - m.canonicalize(); - assert_eq!(m, Null); - - let mut m = X5(Parachain(1), Parent, Parent, Parent, Parachain(3)); - m.canonicalize(); - assert_eq!(m, X3(Parent, Parent, Parachain(3))); - - let mut m = X4(Parachain(1), Parachain(2), Parent, Parent); - m.canonicalize(); - assert_eq!(m, Null); - - let mut m = X4(Parent, Parent, Parachain(1), Parachain(2)); - m.canonicalize(); - assert_eq!(m, X4(Parent, Parent, Parachain(1), Parachain(2))); - } - - #[test] - fn conversion_from_other_types_works() { - use crate::v1::{self, Junction, Junctions}; - use core::convert::TryInto; - - fn takes_multilocation>(_arg: Arg) {} - - takes_multilocation(Null); - takes_multilocation(Parent); - takes_multilocation([Parent, Parachain(4)]); - - assert_eq!(v1::MultiLocation::here().try_into(), Ok(MultiLocation::Null)); - assert_eq!( - v1::MultiLocation::new(1, Junctions::X1(Junction::Parachain(8))).try_into(), - Ok(X2(Parent, Parachain(8))), - ); - assert_eq!( - v1::MultiLocation::new(24, Junctions::Here).try_into(), - Err::(()), - ); - } -} diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs deleted file mode 100644 index 54117e31f9c5..000000000000 --- a/xcm/src/v0/order.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Version 0 of the Cross-Consensus Message format data structures. - -use super::{super::v1::Order as Order1, MultiAsset, MultiLocation, Xcm}; -use alloc::vec::Vec; -use core::{ - convert::{TryFrom, TryInto}, - result, -}; -use derivative::Derivative; -use parity_scale_codec::{self, Decode, Encode}; - -/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages. -#[derive(Derivative, Encode, Decode, scale_info::TypeInfo)] -#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] -#[codec(encode_bound())] -#[codec(decode_bound())] -#[scale_info(bounds(), skip_type_params(Call))] -pub enum Order { - /// Do nothing. Not generally used. - #[codec(index = 0)] - Null, - - /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within - /// this consensus system. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `dest`: The new owner for the assets. - /// - /// Errors: - #[codec(index = 1)] - DepositAsset { assets: Vec, dest: MultiLocation }, - - /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within - /// this consensus system. - /// - /// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `dest`: The new owner for the assets. - /// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to - /// `dest`. - /// - /// Errors: - #[codec(index = 2)] - DepositReserveAsset { assets: Vec, dest: MultiLocation, effects: Vec> }, - - /// Remove the asset(s) (`give`) from holding and replace them with alternative assets. - /// - /// The minimum amount of assets to be received into holding for the order not to fail may be stated. - /// - /// - `give`: The asset(s) to remove from holding. - /// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. The meaning of wildcards - /// is undefined and they should be not be used. - /// - /// Errors: - #[codec(index = 3)] - ExchangeAsset { give: Vec, receive: Vec }, - - /// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account - /// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will - /// be executed on them. There will typically be only one valid location on any given asset/chain combination. - /// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*. - /// - /// Errors: - #[codec(index = 4)] - InitiateReserveWithdraw { - assets: Vec, - reserve: MultiLocation, - effects: Vec>, - }, - - /// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `destination`: A valid location that has a bi-lateral teleportation arrangement. - /// - `effects`: The orders to execute on the assets once arrived *on the destination location*. - /// - /// Errors: - #[codec(index = 5)] - InitiateTeleport { assets: Vec, dest: MultiLocation, effects: Vec> }, - - /// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof. - /// - /// - `query_id`: An identifier that will be replicated into the returned XCM message. - /// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin. - /// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset- - /// wise, *the lesser of this value and the holding account*. No wildcards will be used when reporting assets - /// back. - /// - /// Errors: - #[codec(index = 6)] - QueryHolding { - #[codec(compact)] - query_id: u64, - dest: MultiLocation, - assets: Vec, - }, - - /// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with - /// up to `fees` from the holding account. - /// - /// Errors: - #[codec(index = 7)] - BuyExecution { - fees: MultiAsset, - weight: u64, - debt: u64, - halt_on_error: bool, - xcm: Vec>, - }, -} - -pub mod opaque { - pub type Order = super::Order<()>; -} - -impl Order { - pub fn into(self) -> Order { - Order::from(self) - } - pub fn from(order: Order) -> Self { - use Order::*; - match order { - Null => Null, - DepositAsset { assets, dest } => DepositAsset { assets, dest }, - DepositReserveAsset { assets, dest, effects } => - DepositReserveAsset { assets, dest, effects }, - ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, - InitiateReserveWithdraw { assets, reserve, effects } => - InitiateReserveWithdraw { assets, reserve, effects }, - InitiateTeleport { assets, dest, effects } => - InitiateTeleport { assets, dest, effects }, - QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets }, - BuyExecution { fees, weight, debt, halt_on_error, xcm } => { - let xcm = xcm.into_iter().map(Xcm::from).collect(); - BuyExecution { fees, weight, debt, halt_on_error, xcm } - }, - } - } -} - -impl TryFrom> for Order { - type Error = (); - fn try_from(old: Order1) -> result::Result, ()> { - use Order::*; - Ok(match old { - Order1::Noop => Null, - Order1::DepositAsset { assets, beneficiary, .. } => - DepositAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? }, - Order1::DepositReserveAsset { assets, dest, effects, .. } => DepositReserveAsset { - assets: assets.try_into()?, - dest: dest.try_into()?, - effects: effects - .into_iter() - .map(Order::<()>::try_from) - .collect::>()?, - }, - Order1::ExchangeAsset { give, receive } => - ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? }, - Order1::InitiateReserveWithdraw { assets, reserve, effects } => - InitiateReserveWithdraw { - assets: assets.try_into()?, - reserve: reserve.try_into()?, - effects: effects - .into_iter() - .map(Order::<()>::try_from) - .collect::>()?, - }, - Order1::InitiateTeleport { assets, dest, effects } => InitiateTeleport { - assets: assets.try_into()?, - dest: dest.try_into()?, - effects: effects - .into_iter() - .map(Order::<()>::try_from) - .collect::>()?, - }, - Order1::QueryHolding { query_id, dest, assets } => - QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? }, - Order1::BuyExecution { fees, weight, debt, halt_on_error, instructions } => { - let xcm = instructions - .into_iter() - .map(Xcm::::try_from) - .collect::>()?; - BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, xcm } - }, - }) - } -} diff --git a/xcm/src/v0/traits.rs b/xcm/src/v0/traits.rs deleted file mode 100644 index aafc2820ff17..000000000000 --- a/xcm/src/v0/traits.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Cross-Consensus Message format data structures. - -use core::result; -use parity_scale_codec::{Decode, Encode}; - -use super::{MultiLocation, Xcm}; - -#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)] -pub enum Error { - Undefined, - /// An arithmetic overflow happened. - Overflow, - /// The operation is intentionally unsupported. - Unimplemented, - UnhandledXcmVersion, - /// The implementation does not handle a given XCM. - UnhandledXcmMessage, - /// The implementation does not handle an effect present in an XCM. - UnhandledEffect, - EscalationOfPrivilege, - UntrustedReserveLocation, - UntrustedTeleportLocation, - DestinationBufferOverflow, - /// The message and destination was recognized as being reachable but the operation could not be completed. - /// A human-readable explanation of the specific issue is provided. - SendFailed(#[codec(skip)] &'static str), - /// The message and destination combination was not recognized as being reachable. - NotApplicable(MultiLocation, Xcm<()>), - MultiLocationFull, - FailedToDecode, - BadOrigin, - ExceedsMaxMessageSize, - /// An asset transaction (like withdraw or deposit) failed. - /// See implementers of the `TransactAsset` trait for sources. - /// Causes can include type conversion failures between id or balance types. - FailedToTransactAsset(#[codec(skip)] &'static str), - /// Execution of the XCM would potentially result in a greater weight used than the pre-specified - /// weight limit. The amount that is potentially required is the parameter. - WeightLimitReached(Weight), - /// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a - /// `WithdrawAsset` XCM). - Wildcard, - /// The case where an XCM message has specified a weight limit on an interior call and this - /// limit is too low. - /// - /// Used by: - /// - `Transact` - MaxWeightInvalid, - /// The fees specified by the XCM message were not found in the holding account. - /// - /// Used by: - /// - `BuyExecution` - NotHoldingFees, - /// The weight of an XCM message is not computable ahead of execution. This generally means at least part - /// of the message is invalid, which could be due to it containing overly nested structures or an invalid - /// nested data segment (e.g. for the call in `Transact`). - WeightNotComputable, - /// The XCM did not pass the barrier condition for execution. The barrier condition differs on different - /// chains and in different circumstances, but generally it means that the conditions surrounding the message - /// were not such that the chain considers the message worth spending time executing. Since most chains - /// lift the barrier to execution on appropriate payment, presentation of an NFT voucher, or based on the - /// message origin, it means that none of those were the case. - Barrier, - /// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its - /// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a - /// lock, hold, freeze or is otherwise unavailable. - NotWithdrawable, - /// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location. - LocationCannotHold, - /// The assets given to purchase weight is are insufficient for the weight desired. - TooExpensive, - /// The given asset is not handled. - AssetNotFound, - /// `execute_xcm` has been called too many times recursively. - RecursionLimitReached, -} - -impl From<()> for Error { - fn from(_: ()) -> Self { - Self::Undefined - } -} - -pub type Result = result::Result<(), Error>; - -/// Local weight type; execution time in picoseconds. -pub type Weight = u64; - -/// Outcome of an XCM execution. -#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)] -pub enum Outcome { - /// Execution completed successfully; given weight was used. - Complete(Weight), - /// Execution started, but did not complete successfully due to the given error; given weight was used. - Incomplete(Weight, Error), - /// Execution did not start due to the given error. - Error(Error), -} - -impl Outcome { - pub fn ensure_complete(self) -> Result { - match self { - Outcome::Complete(_) => Ok(()), - Outcome::Incomplete(_, e) => Err(e), - Outcome::Error(e) => Err(e), - } - } - pub fn ensure_execution(self) -> result::Result { - match self { - Outcome::Complete(w) => Ok(w), - Outcome::Incomplete(w, _) => Ok(w), - Outcome::Error(e) => Err(e), - } - } - /// How much weight was used by the XCM execution attempt. - pub fn weight_used(&self) -> Weight { - match self { - Outcome::Complete(w) => *w, - Outcome::Incomplete(w, _) => *w, - Outcome::Error(_) => 0, - } - } -} - -/// Type of XCM message executor. -pub trait ExecuteXcm { - /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is - /// a basic hard-limit and the implementation may place further restrictions or requirements on weight and - /// other aspects. - fn execute_xcm(origin: MultiLocation, message: Xcm, weight_limit: Weight) -> Outcome { - log::debug!( - target: "xcm::execute_xcm", - "origin: {:?}, message: {:?}, weight_limit: {:?}", - origin, - message, - weight_limit, - ); - Self::execute_xcm_in_credit(origin, message, weight_limit, 0) - } - - /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. - /// - /// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow - /// execution without associated payment. - fn execute_xcm_in_credit( - origin: MultiLocation, - message: Xcm, - weight_limit: Weight, - weight_credit: Weight, - ) -> Outcome; -} - -impl ExecuteXcm for () { - fn execute_xcm_in_credit( - _origin: MultiLocation, - _message: Xcm, - _weight_limit: Weight, - _weight_credit: Weight, - ) -> Outcome { - Outcome::Error(Error::Unimplemented) - } -} - -/// Utility for sending an XCM message. -/// -/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return -/// `NotApplicable` to pass the execution to the next sender item. Note that each `NotApplicable` -/// might alter the destination and the XCM message for to the next router. -/// -/// -/// # Example -/// ```rust -/// # use xcm::v0::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result}; -/// # use parity_scale_codec::Encode; -/// -/// /// A sender that only passes the message through and does nothing. -/// struct Sender1; -/// impl SendXcm for Sender1 { -/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { -/// return Err(Error::NotApplicable(destination, message)) -/// } -/// } -/// -/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing. -/// struct Sender2; -/// impl SendXcm for Sender2 { -/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { -/// if let MultiLocation::X2(j1, j2) = destination { -/// Ok(()) -/// } else { -/// Err(Error::Undefined) -/// } -/// } -/// } -/// -/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise. -/// struct Sender3; -/// impl SendXcm for Sender3 { -/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { -/// match destination { -/// MultiLocation::X1(j) if j == Junction::Parent => Ok(()), -/// _ => Err(Error::NotApplicable(destination, message)), -/// } -/// } -/// } -/// -/// // A call to send via XCM. We don't really care about this. -/// # fn main() { -/// let call: Vec = ().encode(); -/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() }; -/// let destination = MultiLocation::X1(Junction::Parent); -/// -/// assert!( -/// // Sender2 will block this. -/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone()) -/// .is_err() -/// ); -/// -/// assert!( -/// // Sender3 will catch this. -/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone()) -/// .is_ok() -/// ); -/// # } -/// ``` -pub trait SendXcm { - /// Send an XCM `message` to a given `destination`. - /// - /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST* - /// return `NotApplicable`. Any other error will cause the tuple implementation to exit early without - /// trying other type fields. - fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl SendXcm for Tuple { - fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { - for_tuples!( #( - // we shadow `destination` and `message` in each expansion for the next one. - let (destination, message) = match Tuple::send_xcm(destination, message) { - Err(Error::NotApplicable(d, m)) => (d, m), - o @ _ => return o, - }; - )* ); - Err(Error::NotApplicable(destination, message)) - } -} diff --git a/xcm/src/v1/junction.rs b/xcm/src/v1/junction.rs index 07d09a0b24e9..af740447daaf 100644 --- a/xcm/src/v1/junction.rs +++ b/xcm/src/v1/junction.rs @@ -17,7 +17,7 @@ //! Support data structures for `MultiLocation`, primarily the `Junction` datatype. use super::{BodyId, BodyPart, Junctions, MultiLocation, NetworkId}; -use crate::{v0::Junction as OldJunction, v3::Junction as NewJunction}; +use crate::v3::Junction as NewJunction; use alloc::vec::Vec; use core::convert::{TryFrom, TryInto}; use parity_scale_codec::{self, Decode, Encode}; @@ -78,26 +78,6 @@ pub enum Junction { Plurality { id: BodyId, part: BodyPart }, } -impl TryFrom for Junction { - type Error = (); - - fn try_from(value: OldJunction) -> Result { - use OldJunction::*; - match value { - Parent => Err(()), - Parachain(id) => Ok(Self::Parachain(id)), - AccountId32 { network, id } => Ok(Self::AccountId32 { network, id }), - AccountIndex64 { network, index } => Ok(Self::AccountIndex64 { network, index }), - AccountKey20 { network, key } => Ok(Self::AccountKey20 { network, key }), - PalletInstance(index) => Ok(Self::PalletInstance(index)), - GeneralIndex(id) => Ok(Self::GeneralIndex(id)), - GeneralKey(key) => Ok(Self::GeneralKey(key)), - OnlyChild => Ok(Self::OnlyChild), - Plurality { id, part } => Ok(Self::Plurality { id: id.into(), part }), - } - } -} - impl TryFrom for Junction { type Error = (); diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index 43e7d504533c..a1c0360068c4 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -60,10 +60,8 @@ //! - v1 Orders that do allow the notion of `All` to be used as wildcards, will instead use a new //! type called `MultiAssetFilter`. -use super::{ - v0::{Response as OldResponse, Xcm as OldXcm}, - v2::{Instruction, Response as NewResponse, Xcm as NewXcm}, -}; +use crate::v2::{Instruction, Response as NewResponse, Xcm as NewXcm}; +use crate::v3::NetworkId as NewNetworkId; use crate::DoubleEncoded; use alloc::vec::Vec; use core::{ @@ -92,9 +90,6 @@ pub use multilocation::{ pub use order::Order; pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm}; -// These parts of XCM v0 have been unchanged in XCM v1, and are re-imported here. -pub use super::v0::{BodyId, BodyPart, NetworkId, OriginKind}; - /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { pub use super::{ @@ -119,6 +114,121 @@ pub mod prelude { }; } +/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`. +#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] +pub enum OriginKind { + /// Origin should just be the native dispatch origin representation for the sender in the + /// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin + /// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a + /// primary/native dispatch origin form. + Native, + + /// Origin should just be the standard account-based origin with the sovereign account of + /// the sender. For Cumulus/Frame chains, this is the `Signed` origin. + SovereignAccount, + + /// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin. + /// This will not usually be an available option. + Superuser, + + /// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be + /// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be + /// the `pallet_xcm::Origin::Xcm` type. + Xcm, +} + +/// A global identifier of an account-bearing consensus system. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +pub enum NetworkId { + /// Unidentified/any. + Any, + /// Some named network. + Named(Vec), + /// The Polkadot Relay chain + Polkadot, + /// Kusama. + Kusama, +} + +impl TryInto for Option { + type Error = (); + fn try_into(self) -> result::Result { + use NewNetworkId::*; + Ok(match self { + None => NetworkId::Any, + Some(Polkadot) => NetworkId::Polkadot, + Some(Kusama) => NetworkId::Kusama, + _ => return Err(()), + }) + } +} + +/// An identifier of a pluralistic body. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +pub enum BodyId { + /// The only body in its context. + Unit, + /// A named body. + Named(Vec), + /// An indexed body. + Index(#[codec(compact)] u32), + /// The unambiguous executive body (for Polkadot, this would be the Polkadot council). + Executive, + /// The unambiguous technical body (for Polkadot, this would be the Technical Committee). + Technical, + /// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of + /// lock-voters). + Legislative, + /// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it + /// may be considered as that). + Judicial, +} + +/// A part of a pluralistic body. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] +pub enum BodyPart { + /// The body's declaration, under whatever means it decides. + Voice, + /// A given number of members of the body. + Members { + #[codec(compact)] + count: u32, + }, + /// A given number of members of the body, out of some larger caucus. + Fraction { + #[codec(compact)] + nom: u32, + #[codec(compact)] + denom: u32, + }, + /// No less than the given proportion of members of the body. + AtLeastProportion { + #[codec(compact)] + nom: u32, + #[codec(compact)] + denom: u32, + }, + /// More than than the given proportion of members of the body. + MoreThanProportion { + #[codec(compact)] + nom: u32, + #[codec(compact)] + denom: u32, + }, +} + +impl BodyPart { + /// Returns `true` if the part represents a strict majority (> 50%) of the body in question. + pub fn is_majority(&self) -> bool { + match self { + BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true, + BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true, + BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true, + _ => false, + } + } +} + /// Response data to a query. #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] pub enum Response { @@ -384,69 +494,6 @@ pub mod opaque { pub use super::order::opaque::*; } -// Convert from a v0 response to a v1 response -impl TryFrom for Response { - type Error = (); - fn try_from(old_response: OldResponse) -> result::Result { - match old_response { - OldResponse::Assets(assets) => Ok(Self::Assets(assets.try_into()?)), - } - } -} - -impl TryFrom> for Xcm { - type Error = (); - fn try_from(old: OldXcm) -> result::Result, ()> { - use Xcm::*; - Ok(match old { - OldXcm::WithdrawAsset { assets, effects } => WithdrawAsset { - assets: assets.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - OldXcm::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited { - assets: assets.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - OldXcm::TeleportAsset { assets, effects } => ReceiveTeleportedAsset { - assets: assets.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - OldXcm::QueryResponse { query_id, response } => - QueryResponse { query_id, response: response.try_into()? }, - OldXcm::TransferAsset { assets, dest } => - TransferAsset { assets: assets.try_into()?, beneficiary: dest.try_into()? }, - OldXcm::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { - assets: assets.try_into()?, - dest: dest.try_into()?, - effects: effects - .into_iter() - .map(Order::try_from) - .collect::>()?, - }, - OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - OldXcm::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, - OldXcm::HrmpChannelClosing { initiator, sender, recipient } => - HrmpChannelClosing { initiator, sender, recipient }, - OldXcm::Transact { origin_type, require_weight_at_most, call } => - Transact { origin_type, require_weight_at_most, call: call.into() }, - OldXcm::RelayedFrom { who, message } => RelayedFrom { - who: MultiLocation::try_from(who)?.try_into()?, - message: alloc::boxed::Box::new((*message).try_into()?), - }, - }) - } -} - impl TryFrom> for Xcm { type Error = (); fn try_from(old: NewXcm) -> result::Result, ()> { diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index 87114c49eee6..fd2f2a4925ec 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -32,7 +32,6 @@ use alloc::{vec, vec::Vec}; use core::{ cmp::Ordering, convert::{TryFrom, TryInto}, - result, }; use parity_scale_codec::{self as codec, Decode, Encode}; use scale_info::TypeInfo; @@ -262,45 +261,6 @@ impl MultiAsset { } } -impl TryFrom for MultiAsset { - type Error = (); - fn try_from(old: super::super::v0::MultiAsset) -> result::Result { - use super::super::v0::MultiAsset as V0; - use AssetId::*; - use Fungibility::*; - let (id, fun) = match old { - V0::ConcreteFungible { id, amount } => (Concrete(id.try_into()?), Fungible(amount)), - V0::ConcreteNonFungible { class, instance } => - (Concrete(class.try_into()?), NonFungible(instance)), - V0::AbstractFungible { id, amount } => (Abstract(id), Fungible(amount)), - V0::AbstractNonFungible { class, instance } => (Abstract(class), NonFungible(instance)), - _ => return Err(()), - }; - Ok(MultiAsset { id, fun }) - } -} - -impl TryFrom for Option { - type Error = (); - fn try_from(old: super::super::v0::MultiAsset) -> result::Result, ()> { - match old { - super::super::v0::MultiAsset::None => return Ok(None), - x => return Ok(Some(x.try_into()?)), - } - } -} - -impl TryFrom> for MultiAsset { - type Error = (); - fn try_from(mut old: Vec) -> result::Result { - if old.len() == 1 { - old.remove(0).try_into() - } else { - Err(()) - } - } -} - impl TryFrom for MultiAsset { type Error = (); fn try_from(new: NewMultiAsset) -> Result { @@ -319,18 +279,6 @@ impl Decode for MultiAssets { } } -impl TryFrom> for MultiAssets { - type Error = (); - fn try_from(old: Vec) -> result::Result { - let v = old - .into_iter() - .map(Option::::try_from) - .filter_map(|x| x.transpose()) - .collect::, ()>>()?; - Ok(v.into()) - } -} - impl TryFrom for MultiAssets { type Error = (); fn try_from(new: NewMultiAssets) -> Result { @@ -503,35 +451,6 @@ pub enum WildMultiAsset { AllOf { id: AssetId, fun: WildFungibility }, } -impl TryFrom for WildMultiAsset { - type Error = (); - fn try_from(old: super::super::v0::MultiAsset) -> result::Result { - use super::super::v0::MultiAsset as V0; - use AssetId::*; - use WildFungibility::*; - let (id, fun) = match old { - V0::All => return Ok(WildMultiAsset::All), - V0::AllConcreteFungible { id } => (Concrete(id.try_into()?), Fungible), - V0::AllConcreteNonFungible { class } => (Concrete(class.try_into()?), NonFungible), - V0::AllAbstractFungible { id } => (Abstract(id), Fungible), - V0::AllAbstractNonFungible { class } => (Abstract(class), NonFungible), - _ => return Err(()), - }; - Ok(WildMultiAsset::AllOf { id, fun }) - } -} - -impl TryFrom> for WildMultiAsset { - type Error = (); - fn try_from(mut old: Vec) -> result::Result { - if old.len() == 1 { - old.remove(0).try_into() - } else { - Err(()) - } - } -} - impl WildMultiAsset { /// Returns true if `self` is a super-set of the given `inner`. /// @@ -618,19 +537,6 @@ impl MultiAssetFilter { } } -impl TryFrom> for MultiAssetFilter { - type Error = (); - fn try_from( - mut old: Vec, - ) -> result::Result { - if old.len() == 1 && old[0].is_wildcard() { - Ok(MultiAssetFilter::Wild(old.remove(0).try_into()?)) - } else { - Ok(MultiAssetFilter::Definite(old.try_into()?)) - } - } -} - impl TryFrom for WildMultiAsset { type Error = (); fn try_from(new: NewWildMultiAsset) -> Result { diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index eb4b8efe7dae..5043b70c951f 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -1036,9 +1036,6 @@ mod tests { #[test] fn conversion_from_other_types_works() { - use crate::v0; - use core::convert::TryInto; - fn takes_multilocation>(_arg: Arg) {} takes_multilocation(Parent); @@ -1055,24 +1052,5 @@ mod tests { takes_multilocation((Parent, Here)); takes_multilocation(ParentThen(X1(Parachain(75)))); takes_multilocation([Parachain(100), PalletInstance(3)]); - - assert_eq!(v0::MultiLocation::Null.try_into(), Ok(MultiLocation::here())); - assert_eq!( - v0::MultiLocation::X1(v0::Junction::Parent).try_into(), - Ok(MultiLocation::parent()) - ); - assert_eq!( - v0::MultiLocation::X2(v0::Junction::Parachain(88), v0::Junction::Parent).try_into(), - Ok(MultiLocation::here()), - ); - assert_eq!( - v0::MultiLocation::X3( - v0::Junction::Parent, - v0::Junction::Parent, - v0::Junction::GeneralKey(b"foo".to_vec()), - ) - .try_into(), - Ok(MultiLocation { parents: 2, interior: X1(GeneralKey(b"foo".to_vec())) }), - ); } } diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index 00ee69458cd0..77d7d8330c34 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -17,10 +17,10 @@ //! Version 1 of the Cross-Consensus Message format data structures. use super::{MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm}; -use crate::{v0::Order as OldOrder, v2::Instruction}; +use crate::v2::Instruction; use alloc::{vec, vec::Vec}; use core::{ - convert::{TryFrom, TryInto}, + convert::TryFrom, result, }; use derivative::Derivative; @@ -185,56 +185,6 @@ impl Order { } } -impl TryFrom> for Order { - type Error = (); - fn try_from(old: OldOrder) -> result::Result, ()> { - use Order::*; - Ok(match old { - OldOrder::Null => Noop, - OldOrder::DepositAsset { assets, dest } => DepositAsset { - assets: assets.try_into()?, - max_assets: 1, - beneficiary: dest.try_into()?, - }, - OldOrder::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset { - assets: assets.try_into()?, - max_assets: 1, - dest: dest.try_into()?, - effects: effects - .into_iter() - .map(Order::<()>::try_from) - .collect::>()?, - }, - OldOrder::ExchangeAsset { give, receive } => - ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? }, - OldOrder::InitiateReserveWithdraw { assets, reserve, effects } => - InitiateReserveWithdraw { - assets: assets.try_into()?, - reserve: reserve.try_into()?, - effects: effects - .into_iter() - .map(Order::<()>::try_from) - .collect::>()?, - }, - OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport { - assets: assets.try_into()?, - dest: dest.try_into()?, - effects: effects - .into_iter() - .map(Order::<()>::try_from) - .collect::>()?, - }, - OldOrder::QueryHolding { query_id, dest, assets } => - QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? }, - OldOrder::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { - let instructions = - xcm.into_iter().map(Xcm::::try_from).collect::>()?; - BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, instructions } - }, - }) - } -} - impl TryFrom> for Order { type Error = (); fn try_from(old: Instruction) -> result::Result, ()> { From 087ac70e28b0c06cc039570c40d18ab78a3c6951 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 7 Feb 2022 19:33:59 +0000 Subject: [PATCH 47/57] Minor grumbles --- xcm/src/v3/junction.rs | 4 ++-- xcm/src/v3/junctions.rs | 14 +------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/xcm/src/v3/junction.rs b/xcm/src/v3/junction.rs index 030f1b786881..d0b323782444 100644 --- a/xcm/src/v3/junction.rs +++ b/xcm/src/v3/junction.rs @@ -191,12 +191,12 @@ impl Junction { /// Convert `self` into a `MultiLocation` containing `n` parents. /// - /// Similar to `Self::into`, with the added ability to specify the number of parent junctions. + /// Similar to `Self::into_location`, with the added ability to specify the number of parent junctions. pub const fn into_exterior(self, n: u8) -> MultiLocation { MultiLocation { parents: n, interior: Junctions::X1(self) } } - /// Convert `self` into a `MultiLocation` containing 0 parents. + /// Convert `self` into a `VersionedMultiLocation` containing 0 parents. /// /// Similar to `Into::into`, except that this method can be used in a const evaluation context. pub const fn into_versioned(self) -> VersionedMultiLocation { diff --git a/xcm/src/v3/junctions.rs b/xcm/src/v3/junctions.rs index b223dce60eca..9ad0171c82a9 100644 --- a/xcm/src/v3/junctions.rs +++ b/xcm/src/v3/junctions.rs @@ -124,7 +124,7 @@ impl Junctions { /// Convert `self` into a `MultiLocation` containing `n` parents. /// - /// Similar to `Self::into`, with the added ability to specify the number of parent junctions. + /// Similar to `Self::into_location`, with the added ability to specify the number of parent junctions. pub const fn into_exterior(self, n: u8) -> MultiLocation { MultiLocation { parents: n, interior: self } } @@ -440,18 +440,6 @@ impl Junctions { JunctionsRefIterator { junctions: self, next: 0, back: 0 } } - /// Returns a reference iterator over the junctions in reverse. - #[deprecated(note = "Please use iter().rev()")] - pub fn iter_rev(&self) -> impl Iterator + '_ { - self.iter().rev() - } - - /// Consumes `self` and returns an iterator over the junctions in reverse. - #[deprecated(note = "Please use into_iter().rev()")] - pub fn into_iter_rev(self) -> impl Iterator { - self.into_iter().rev() - } - /// Ensures that self begins with `prefix` and that it has a single `Junction` item following. /// If so, returns a reference to this `Junction` item. /// From 9c9c38f2e48a2fe067e702c4daecc72b7e9d851f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 8 Feb 2022 12:08:31 +0100 Subject: [PATCH 48/57] Formatting --- xcm/src/v1/mod.rs | 8 +++++--- xcm/src/v1/order.rs | 5 +---- xcm/src/v3/traits.rs | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index a1c0360068c4..949403c83c5f 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -60,9 +60,11 @@ //! - v1 Orders that do allow the notion of `All` to be used as wildcards, will instead use a new //! type called `MultiAssetFilter`. -use crate::v2::{Instruction, Response as NewResponse, Xcm as NewXcm}; -use crate::v3::NetworkId as NewNetworkId; -use crate::DoubleEncoded; +use crate::{ + v2::{Instruction, Response as NewResponse, Xcm as NewXcm}, + v3::NetworkId as NewNetworkId, + DoubleEncoded, +}; use alloc::vec::Vec; use core::{ convert::{TryFrom, TryInto}, diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index 77d7d8330c34..66f90395d8da 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -19,10 +19,7 @@ use super::{MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm}; use crate::v2::Instruction; use alloc::{vec, vec::Vec}; -use core::{ - convert::TryFrom, - result, -}; +use core::{convert::TryFrom, result}; use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; use scale_info::TypeInfo; diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 36f2b6f53e3f..13af3f80763f 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -167,9 +167,8 @@ impl TryFrom for Error { impl From for Error { fn from(e: SendError) -> Self { match e { - SendError::NotApplicable | - SendError::Unroutable | - SendError::MissingArgument => Error::Unroutable, + SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument => + Error::Unroutable, SendError::Transport(s) => Error::Transport(s), SendError::DestinationUnsupported => Error::DestinationUnsupported, SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, From ca0d360a436cfb421b1e8956eaf3fcc8a11b5953 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 8 Feb 2022 15:00:19 +0100 Subject: [PATCH 49/57] Formatting --- Cargo.lock | 4 +-- bridges/primitives/messages/Cargo.toml | 2 +- runtime/common/Cargo.toml | 2 +- runtime/common/src/xcm_sender.rs | 2 +- runtime/test-runtime/src/xcm_config.rs | 2 +- xcm/Cargo.toml | 2 +- xcm/pallet-xcm-benchmarks/src/mock.rs | 2 +- xcm/pallet-xcm/src/mock.rs | 4 +-- xcm/src/v1/mod.rs | 8 +++-- xcm/src/v1/order.rs | 5 +-- xcm/src/v3/traits.rs | 41 ++++++++++------------- xcm/xcm-builder/Cargo.toml | 2 +- xcm/xcm-builder/src/bridging_tests/mod.rs | 6 ++-- xcm/xcm-builder/src/mock.rs | 4 +-- xcm/xcm-builder/src/universal_exports.rs | 20 +++++------ xcm/xcm-builder/tests/mock/mod.rs | 2 +- xcm/xcm-executor/Cargo.toml | 2 +- xcm/xcm-executor/src/traits/export.rs | 22 ++++++------ xcm/xcm-simulator/src/lib.rs | 4 +-- 19 files changed, 64 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c2dbf5f5771..58b401b0e30e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2812,9 +2812,9 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", diff --git a/bridges/primitives/messages/Cargo.toml b/bridges/primitives/messages/Cargo.toml index 31ec46222cd8..c80a4b7776f0 100644 --- a/bridges/primitives/messages/Cargo.toml +++ b/bridges/primitives/messages/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] bitvec = { version = "0.20", default-features = false, features = ["alloc"] } codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false, features = ["derive", "bit-vec"] } -impl-trait-for-tuples = "0.2" +impl-trait-for-tuples = "0.2.2" scale-info = { version = "1.0", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index 69b2debdb64a..a9351c112aab 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -impl-trait-for-tuples = "0.2.0" +impl-trait-for-tuples = "0.2.2" bitvec = { version = "0.20.1", default-features = false, features = ["alloc"] } parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] } log = { version = "0.4.13", default-features = false } diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 57cae59474d7..f93fa4944dfa 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -32,7 +32,7 @@ pub struct ChildParachainRouter(PhantomData<(T, W)>); impl SendXcm for ChildParachainRouter { - type OptionTicket = Option<(HostConfiguration, ParaId, Vec)>; + type Ticket = (HostConfiguration, ParaId, Vec); fn validate( dest: &mut Option, diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index 332860cb7956..dfd70159d152 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -41,7 +41,7 @@ pub type LocalOriginToLocation = ( pub struct DoNothingRouter; impl SendXcm for DoNothingRouter { - type OptionTicket = Option<()>; + type Ticket = (); fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult<()> { Ok(((), MultiAssets::new())) } diff --git a/xcm/Cargo.toml b/xcm/Cargo.toml index 74fba460bdd9..44cd23db3a1a 100644 --- a/xcm/Cargo.toml +++ b/xcm/Cargo.toml @@ -6,7 +6,7 @@ description = "The basic XCM datastructures." edition = "2018" [dependencies] -impl-trait-for-tuples = "0.2.0" +impl-trait-for-tuples = "0.2.2" parity-scale-codec = { version = "2.3.1", default-features = false, features = [ "derive" ] } scale-info = { version = "1.0", default-features = false, features = ["derive"] } derivative = {version = "2.2.0", default-features = false, features = [ "use_core" ] } diff --git a/xcm/pallet-xcm-benchmarks/src/mock.rs b/xcm/pallet-xcm-benchmarks/src/mock.rs index 4892a04f9efa..85bbea99d775 100644 --- a/xcm/pallet-xcm-benchmarks/src/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/mock.rs @@ -21,7 +21,7 @@ use xcm_executor::traits::FilterAssetLocation; // An xcm sender/receiver akin to > /dev/null pub struct DevNull; impl xcm::opaque::latest::SendXcm for DevNull { - type OptionTicket = Option<()>; + type Ticket = (); fn validate(_: &mut Option, _: &mut Option>) -> SendResult<()> { Ok(((), MultiAssets::new())) } diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index f23497353812..74036e53c4ae 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -156,7 +156,7 @@ pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { /// Sender that never returns error, always sends pub struct TestSendXcm; impl SendXcm for TestSendXcm { - type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + type Ticket = (MultiLocation, Xcm<()>); fn validate( dest: &mut Option, msg: &mut Option>, @@ -172,7 +172,7 @@ impl SendXcm for TestSendXcm { /// Sender that returns error if `X8` junction and stops routing pub struct TestSendXcmErrX8; impl SendXcm for TestSendXcmErrX8 { - type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + type Ticket = (MultiLocation, Xcm<()>); fn validate( dest: &mut Option, msg: &mut Option>, diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index a1c0360068c4..949403c83c5f 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -60,9 +60,11 @@ //! - v1 Orders that do allow the notion of `All` to be used as wildcards, will instead use a new //! type called `MultiAssetFilter`. -use crate::v2::{Instruction, Response as NewResponse, Xcm as NewXcm}; -use crate::v3::NetworkId as NewNetworkId; -use crate::DoubleEncoded; +use crate::{ + v2::{Instruction, Response as NewResponse, Xcm as NewXcm}, + v3::NetworkId as NewNetworkId, + DoubleEncoded, +}; use alloc::vec::Vec; use core::{ convert::{TryFrom, TryInto}, diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index 77d7d8330c34..66f90395d8da 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -19,10 +19,7 @@ use super::{MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm}; use crate::v2::Instruction; use alloc::{vec, vec::Vec}; -use core::{ - convert::TryFrom, - result, -}; +use core::{convert::TryFrom, result}; use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; use scale_info::TypeInfo; diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 36f2b6f53e3f..1072d140b980 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -167,9 +167,8 @@ impl TryFrom for Error { impl From for Error { fn from(e: SendError) -> Self { match e { - SendError::NotApplicable | - SendError::Unroutable | - SendError::MissingArgument => Error::Unroutable, + SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument => + Error::Unroutable, SendError::Transport(s) => Error::Transport(s), SendError::DestinationUnsupported => Error::DestinationUnsupported, SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, @@ -353,7 +352,7 @@ impl Unwrappable for Option { /// /// A sender that only passes the message through and does nothing. /// struct Sender1; /// impl SendXcm for Sender1 { -/// type OptionTicket = Option; +/// type Ticket = Infallible; /// fn validate(_: &mut Option, _: &mut Option>) -> SendResult { /// Err(SendError::NotApplicable) /// } @@ -365,7 +364,7 @@ impl Unwrappable for Option { /// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing. /// struct Sender2; /// impl SendXcm for Sender2 { -/// type OptionTicket = Option<()>; +/// type Ticket = (); /// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { /// match destination.as_ref().ok_or(SendError::MissingArgument)? { /// MultiLocation { parents: 0, interior: X2(j1, j2) } => Ok(((), MultiAssets::new())), @@ -380,7 +379,7 @@ impl Unwrappable for Option { /// /// A sender that accepts a message from a parent, passing through otherwise. /// struct Sender3; /// impl SendXcm for Sender3 { -/// type OptionTicket = Option<()>; +/// type Ticket = (); /// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { /// match destination.as_ref().ok_or(SendError::MissingArgument)? { /// MultiLocation { parents: 1, interior: Here } => Ok(((), MultiAssets::new())), @@ -409,7 +408,7 @@ impl Unwrappable for Option { /// # } /// ``` pub trait SendXcm { - type OptionTicket: Unwrappable; + type Ticket; /// Check whether the given `_message` is deliverable to the given `_destination` and if so /// determine the cost which will be paid by this chain to do so, returning a `Validated` token @@ -424,32 +423,31 @@ pub trait SendXcm { fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult<::Inner>; + ) -> SendResult; /// Actually carry out the delivery operation for a previously validated message sending. - fn deliver(ticket: ::Inner) - -> result::Result<(), SendError>; + fn deliver(ticket: Self::Ticket) -> result::Result<(), SendError>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl SendXcm for Tuple { - type OptionTicket = Option<(for_tuples! { #( Tuple::OptionTicket ),* })>; + for_tuples! { type Ticket = (#( Option ),* ); } fn validate( destination: &mut Option, message: &mut Option>, - ) -> SendResult<(for_tuples! { #( Tuple::OptionTicket ),* })> { + ) -> SendResult { let mut maybe_cost: Option = None; - let one_ticket: (for_tuples! { #( Tuple::OptionTicket ),* }) = (for_tuples! { #( + let one_ticket: Self::Ticket = (for_tuples! { #( if maybe_cost.is_some() { - ::none() + None } else { match Tuple::validate(destination, message) { - Err(SendError::NotApplicable) => ::none(), + Err(SendError::NotApplicable) => None, Err(e) => { return Err(e) }, Ok((v, c)) => { maybe_cost = Some(c); - ::some(v) + Some(v) }, } } @@ -461,11 +459,9 @@ impl SendXcm for Tuple { } } - fn deliver( - one_ticket: ::Inner, - ) -> result::Result<(), SendError> { + fn deliver(one_ticket: Self::Ticket) -> result::Result<(), SendError> { for_tuples!( #( - if let Some(validated) = one_ticket.Tuple.take() { + if let Some(validated) = one_ticket.Tuple { return Tuple::deliver(validated); } )* ); @@ -475,10 +471,7 @@ impl SendXcm for Tuple { /// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps /// both in `Some` before passing them as as mutable references into `T::send_xcm`. -pub fn validate_send( - dest: MultiLocation, - msg: Xcm<()>, -) -> SendResult<::Inner> { +pub fn validate_send(dest: MultiLocation, msg: Xcm<()>) -> SendResult { T::validate(&mut Some(dest), &mut Some(msg)) } diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index a0326f839d34..3f91709bbc3e 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -6,7 +6,7 @@ description = "Tools & types for building with XCM and its executor." version = "0.9.13" [dependencies] -impl-trait-for-tuples = "0.2.1" +impl-trait-for-tuples = "0.2.2" parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] } scale-info = { version = "1.0", default-features = false, features = ["derive"] } xcm = { path = "..", default-features = false } diff --git a/xcm/xcm-builder/src/bridging_tests/mod.rs b/xcm/xcm-builder/src/bridging_tests/mod.rs index 674c1ca33e6d..6e340ff613d1 100644 --- a/xcm/xcm-builder/src/bridging_tests/mod.rs +++ b/xcm/xcm-builder/src/bridging_tests/mod.rs @@ -61,7 +61,7 @@ std::thread_local! { } struct TestRemoteIncomingRouter; impl SendXcm for TestRemoteIncomingRouter { - type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + type Ticket = (MultiLocation, Xcm<()>); fn validate( dest: &mut Option, msg: &mut Option>, @@ -107,7 +107,7 @@ fn deliver( impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { - type OptionTicket = Option>; + type Ticket = Xcm<()>; fn validate( destination: &mut Option, @@ -147,7 +147,7 @@ struct ExecutingRouter(PhantomData<(Local, Remote impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { - type OptionTicket = Option>; + type Ticket = Xcm<()>; fn validate( destination: &mut Option, diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index cd879908ffbf..66d393d0ae73 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -128,7 +128,7 @@ pub fn clear_exporter_override() { } pub struct TestMessageSender; impl SendXcm for TestMessageSender { - type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + type Ticket = (MultiLocation, Xcm<()>); fn validate( dest: &mut Option, msg: &mut Option>, @@ -143,7 +143,7 @@ impl SendXcm for TestMessageSender { } pub struct TestMessageExporter; impl ExportXcm for TestMessageExporter { - type OptionTicket = Option<(NetworkId, u32, InteriorMultiLocation, Xcm<()>)>; + type Ticket = (NetworkId, u32, InteriorMultiLocation, Xcm<()>); fn validate( network: NetworkId, channel: u32, diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index 8c36c6ed4c81..d88329a4128c 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -59,12 +59,12 @@ pub struct LocalUnpaidExporter(PhantomData<(Exporter, Ancest impl> SendXcm for LocalUnpaidExporter { - type OptionTicket = Exporter::OptionTicket; + type Ticket = Exporter::Ticket; fn validate( dest: &mut Option, xcm: &mut Option>, - ) -> SendResult<::Inner> { + ) -> SendResult { let d = dest.take().ok_or(MissingArgument)?; let devolved = match ensure_is_remote(Ancestry::get(), d) { Ok(x) => x, @@ -84,7 +84,7 @@ impl> SendXcm validate_export::(network, 0, destination, message) } - fn deliver(ticket: ::Inner) -> Result<(), SendError> { + fn deliver(ticket: Exporter::Ticket) -> Result<(), SendError> { Exporter::deliver(ticket) } } @@ -150,12 +150,12 @@ pub struct UnpaidRemoteExporter( impl> SendXcm for UnpaidRemoteExporter { - type OptionTicket = Router::OptionTicket; + type Ticket = Router::Ticket; fn validate( dest: &mut Option, xcm: &mut Option>, - ) -> SendResult<::Inner> { + ) -> SendResult { let d = dest.as_ref().ok_or(MissingArgument)?.clone(); let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| NotApplicable)?; let (remote_network, remote_location, local_network, local_location) = devolved; @@ -190,7 +190,7 @@ impl Ok((v, cost)) } - fn deliver(validation: ::Inner) -> Result<(), SendError> { + fn deliver(validation: Router::Ticket) -> Result<(), SendError> { Router::deliver(validation) } } @@ -209,12 +209,12 @@ pub struct SovereignPaidRemoteExporter( impl> SendXcm for SovereignPaidRemoteExporter { - type OptionTicket = Router::OptionTicket; + type Ticket = Router::Ticket; fn validate( dest: &mut Option, xcm: &mut Option>, - ) -> SendResult<::Inner> { + ) -> SendResult { let d = dest.as_ref().ok_or(MissingArgument)?.clone(); let devolved = ensure_is_remote(Ancestry::get(), d).map_err(|_| NotApplicable)?; let (remote_network, remote_location, local_network, local_location) = devolved; @@ -264,7 +264,7 @@ impl Ok((v, cost)) } - fn deliver(ticket: ::Inner) -> Result<(), SendError> { + fn deliver(ticket: Router::Ticket) -> Result<(), SendError> { Router::deliver(ticket) } } @@ -331,7 +331,7 @@ pub struct HaulBlobExporter( impl, Price: Get> ExportXcm for HaulBlobExporter { - type OptionTicket = Option>; + type Ticket = Vec; fn validate( network: NetworkId, diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index 2aa187a9400c..730663a86086 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -47,7 +47,7 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { } pub struct TestSendXcm; impl SendXcm for TestSendXcm { - type OptionTicket = Option<(MultiLocation, Xcm<()>)>; + type Ticket = (MultiLocation, Xcm<()>); fn validate( dest: &mut Option, msg: &mut Option>, diff --git a/xcm/xcm-executor/Cargo.toml b/xcm/xcm-executor/Cargo.toml index 04006ce65afb..f388d25ea629 100644 --- a/xcm/xcm-executor/Cargo.toml +++ b/xcm/xcm-executor/Cargo.toml @@ -6,7 +6,7 @@ description = "An abstract and configurable XCM message executor." version = "0.9.13" [dependencies] -impl-trait-for-tuples = "0.2.0" +impl-trait-for-tuples = "0.2.2" parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] } xcm = { path = "..", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index 661af6cf9313..65823b8b3c14 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -17,7 +17,7 @@ use xcm::latest::prelude::*; pub trait ExportXcm { - type OptionTicket: Unwrappable; + type Ticket; /// Check whether the given `_message` is deliverable to the given `_destination` and if so /// determine the cost which will be paid by this chain to do so, returning a `Validated` token @@ -34,37 +34,37 @@ pub trait ExportXcm { channel: u32, destination: &mut Option, message: &mut Option>, - ) -> SendResult<::Inner>; + ) -> SendResult; /// Actually carry out the delivery operation for a previously validated message sending. /// /// The implementation should do everything possible to ensure that this function is infallible /// if called immediately after `validate`. Returning an error here would result in a price /// paid without the service being delivered. - fn deliver(ticket: ::Inner) -> Result<(), SendError>; + fn deliver(ticket: Self::Ticket) -> Result<(), SendError>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl ExportXcm for Tuple { - type OptionTicket = Option<(for_tuples! { #( Tuple::OptionTicket ),* })>; + for_tuples! { type Ticket = (#( Option ),* ); } fn validate( network: NetworkId, channel: u32, destination: &mut Option, message: &mut Option>, - ) -> SendResult<(for_tuples! { #( Tuple::OptionTicket ),* })> { + ) -> SendResult { let mut maybe_cost: Option = None; - let one_ticket: (for_tuples! { #( Tuple::OptionTicket ),* }) = (for_tuples! { #( + let one_ticket: Self::Ticket = (for_tuples! { #( if maybe_cost.is_some() { - ::none() + None } else { match Tuple::validate(network, channel, destination, message) { - Err(SendError::NotApplicable) => ::none(), + Err(SendError::NotApplicable) => None, Err(e) => { return Err(e) }, Ok((v, c)) => { maybe_cost = Some(c); - ::some(v) + Some(v) }, } } @@ -76,7 +76,7 @@ impl ExportXcm for Tuple { } } - fn deliver(one_ticket: ::Inner) -> Result<(), SendError> { + fn deliver(one_ticket: Self::Ticket) -> Result<(), SendError> { for_tuples!( #( if let Some(validated) = one_ticket.Tuple.take() { return Tuple::deliver(validated); @@ -93,7 +93,7 @@ pub fn validate_export( channel: u32, dest: InteriorMultiLocation, msg: Xcm<()>, -) -> SendResult<::Inner> { +) -> SendResult { T::validate(network, channel, &mut Some(dest), &mut Some(msg)) } diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 9b9dc5482279..05effd284ec3 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -296,7 +296,7 @@ macro_rules! decl_test_network { pub struct ParachainXcmRouter($crate::PhantomData); impl> $crate::SendXcm for ParachainXcmRouter { - type OptionTicket = Option<($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>)>; + type Ticket = ($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>); fn validate( destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>, @@ -328,7 +328,7 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; impl $crate::SendXcm for RelayChainXcmRouter { - type OptionTicket = Option<($crate::MultiLocation, $crate::Xcm<()>)>; + type Ticket = ($crate::MultiLocation, $crate::Xcm<()>); fn validate( destination: &mut Option<$crate::MultiLocation>, message: &mut Option<$crate::Xcm<()>>, From 3570fb4889eb7af5f93bee1fd5f8f4cab847f1f1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 8 Feb 2022 19:58:02 +0100 Subject: [PATCH 50/57] Fixes --- xcm/pallet-xcm/src/tests.rs | 96 ++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index a67ec11ff75f..5739f57f0a97 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -336,7 +336,7 @@ fn teleport_assets_works() { )] ); let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1); - let _check_v0_ok: xcm::v0::Xcm<()> = versioned_sent.try_into().unwrap(); + let _check_v1_ok: xcm::v1::Xcm<()> = versioned_sent.try_into().unwrap(); assert_eq!( last_event(), Event::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight))) @@ -378,7 +378,7 @@ fn limmited_teleport_assets_works() { )] ); let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1); - let _check_v0_ok: xcm::v0::Xcm<()> = versioned_sent.try_into().unwrap(); + let _check_v1_ok: xcm::v1::Xcm<()> = versioned_sent.try_into().unwrap(); assert_eq!( last_event(), Event::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight))) @@ -463,7 +463,7 @@ fn reserve_transfer_assets_works() { )] ); let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1); - let _check_v0_ok: xcm::v0::Xcm<()> = versioned_sent.try_into().unwrap(); + let _check_v1_ok: xcm::v1::Xcm<()> = versioned_sent.try_into().unwrap(); assert_eq!( last_event(), Event::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight))) @@ -509,7 +509,7 @@ fn limited_reserve_transfer_assets_works() { )] ); let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1); - let _check_v0_ok: xcm::v0::Xcm<()> = versioned_sent.try_into().unwrap(); + let _check_v1_ok: xcm::v1::Xcm<()> = versioned_sent.try_into().unwrap(); assert_eq!( last_event(), Event::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight))) @@ -852,38 +852,29 @@ fn subscription_side_upgrades_work_with_notify() { new_test_ext_with_balances(vec![]).execute_with(|| { AdvertisedXcmVersion::set(1); - // An entry from a previous runtime with v0 XCM. - let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000)); - let v0_location = VersionedMultiLocation::from(v0_location); - VersionNotifyTargets::::insert(0, v0_location, (69, 0, 1)); - let v1_location = Parachain(1001).into_versioned(); - VersionNotifyTargets::::insert(1, v1_location, (70, 0, 1)); - let v2_location = Parachain(1002).into_versioned(); - VersionNotifyTargets::::insert(2, v2_location, (71, 0, 1)); + // An entry from a previous runtime with v1 XCM. + let v1_location = VersionedMultiLocation::V1(xcm::v1::Junction::Parachain(1001).into()); + VersionNotifyTargets::::insert(1, v1_location, (70, 0, 2)); + let v3_location = Parachain(1003).into_versioned(); + VersionNotifyTargets::::insert(3, v3_location, (72, 0, 2)); // New version. - AdvertisedXcmVersion::set(2); + AdvertisedXcmVersion::set(3); // A runtime upgrade which alters the version does send notifications. XcmPallet::on_runtime_upgrade(); XcmPallet::on_initialize(1); - let instr0 = QueryResponse { - query_id: 69, - max_weight: 0, - response: Response::Version(2), - querier: None, - }; let instr1 = QueryResponse { query_id: 70, max_weight: 0, - response: Response::Version(2), + response: Response::Version(3), querier: None, }; - let instr2 = QueryResponse { - query_id: 71, + let instr3 = QueryResponse { + query_id: 72, max_weight: 0, - response: Response::Version(2), + response: Response::Version(3), querier: None, }; let mut sent = take_sent_xcm(); @@ -894,9 +885,8 @@ fn subscription_side_upgrades_work_with_notify() { assert_eq!( sent, vec![ - (Parachain(1000).into(), Xcm(vec![instr0])), (Parachain(1001).into(), Xcm(vec![instr1])), - (Parachain(1002).into(), Xcm(vec![instr2])), + (Parachain(1003).into(), Xcm(vec![instr3])), ] ); @@ -905,9 +895,8 @@ fn subscription_side_upgrades_work_with_notify() { assert_eq!( contents, vec![ - (XCM_VERSION, Parachain(1000).into_versioned(), (69, 0, 2)), - (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 2)), - (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 2)), + (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 3)), + (XCM_VERSION, Parachain(1003).into_versioned(), (72, 0, 3)), ] ); }); @@ -916,14 +905,11 @@ fn subscription_side_upgrades_work_with_notify() { #[test] fn subscription_side_upgrades_work_without_notify() { new_test_ext_with_balances(vec![]).execute_with(|| { - // An entry from a previous runtime with v0 XCM. - let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000)); - let v0_location = VersionedMultiLocation::from(v0_location); - VersionNotifyTargets::::insert(0, v0_location, (69, 0, 2)); - let v1_location = Parachain(1001).into_versioned(); + // An entry from a previous runtime with v1 XCM. + let v1_location = VersionedMultiLocation::V1(xcm::v1::Junction::Parachain(1001).into()); VersionNotifyTargets::::insert(1, v1_location, (70, 0, 2)); - let v2_location = Parachain(1002).into_versioned(); - VersionNotifyTargets::::insert(2, v2_location, (71, 0, 2)); + let v3_location = Parachain(1003).into_versioned(); + VersionNotifyTargets::::insert(3, v3_location, (72, 0, 2)); // A runtime upgrade which alters the version does send notifications. XcmPallet::on_runtime_upgrade(); @@ -934,9 +920,8 @@ fn subscription_side_upgrades_work_without_notify() { assert_eq!( contents, vec![ - (XCM_VERSION, Parachain(1000).into_versioned(), (69, 0, 2)), - (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 2)), - (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 2)), + (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 3)), + (XCM_VERSION, Parachain(1003).into_versioned(), (72, 0, 3)), ] ); }); @@ -1086,16 +1071,15 @@ fn subscription_side_upgrades_work_with_multistage_notify() { AdvertisedXcmVersion::set(1); // An entry from a previous runtime with v0 XCM. - let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000)); - let v0_location = VersionedMultiLocation::from(v0_location); - VersionNotifyTargets::::insert(0, v0_location, (69, 0, 1)); - let v1_location = Parachain(1001).into_versioned(); + let v1_location = VersionedMultiLocation::V1(xcm::v1::Junction::Parachain(1001).into()); VersionNotifyTargets::::insert(1, v1_location, (70, 0, 1)); - let v2_location = Parachain(1002).into_versioned(); + let v2_location = VersionedMultiLocation::V1(xcm::v2::Junction::Parachain(1002).into()); VersionNotifyTargets::::insert(2, v2_location, (71, 0, 1)); + let v3_location = Parachain(1003).into_versioned(); + VersionNotifyTargets::::insert(3, v3_location, (72, 0, 1)); // New version. - AdvertisedXcmVersion::set(2); + AdvertisedXcmVersion::set(3); // A runtime upgrade which alters the version does send notifications. XcmPallet::on_runtime_upgrade(); @@ -1108,22 +1092,22 @@ fn subscription_side_upgrades_work_with_multistage_notify() { } assert_eq!(counter, 4); - let instr0 = QueryResponse { - query_id: 69, - max_weight: 0, - response: Response::Version(2), - querier: None, - }; let instr1 = QueryResponse { query_id: 70, max_weight: 0, - response: Response::Version(2), + response: Response::Version(3), querier: None, }; let instr2 = QueryResponse { query_id: 71, max_weight: 0, - response: Response::Version(2), + response: Response::Version(3), + querier: None, + }; + let instr3 = QueryResponse { + query_id: 72, + max_weight: 0, + response: Response::Version(3), querier: None, }; let mut sent = take_sent_xcm(); @@ -1134,9 +1118,9 @@ fn subscription_side_upgrades_work_with_multistage_notify() { assert_eq!( sent, vec![ - (Parachain(1000).into(), Xcm(vec![instr0])), (Parachain(1001).into(), Xcm(vec![instr1])), (Parachain(1002).into(), Xcm(vec![instr2])), + (Parachain(1003).into(), Xcm(vec![instr3])), ] ); @@ -1145,9 +1129,9 @@ fn subscription_side_upgrades_work_with_multistage_notify() { assert_eq!( contents, vec![ - (XCM_VERSION, Parachain(1000).into_versioned(), (69, 0, 2)), - (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 2)), - (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 2)), + (XCM_VERSION, Parachain(1001).into_versioned(), (70, 0, 3)), + (XCM_VERSION, Parachain(1002).into_versioned(), (71, 0, 3)), + (XCM_VERSION, Parachain(1003).into_versioned(), (72, 0, 3)), ] ); }); From 45a697c8c9c1172289b0b660c1d925455279e1bf Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 8 Feb 2022 23:00:24 +0100 Subject: [PATCH 51/57] Fixes --- xcm/pallet-xcm/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 74036e53c4ae..00c409aa4c3a 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -299,7 +299,7 @@ impl xcm_executor::Config for XcmConfig { pub type LocalOriginToLocation = SignedToAccountId32; parameter_types! { - pub static AdvertisedXcmVersion: pallet_xcm::XcmVersion = 2; + pub static AdvertisedXcmVersion: pallet_xcm::XcmVersion = 3; } impl pallet_xcm::Config for Test { From e28c84caef1cba7037d20b595e8f24ac5c88b3e5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 9 Feb 2022 15:58:43 +0100 Subject: [PATCH 52/57] Cleanup XCM config --- runtime/kusama/src/xcm_config.rs | 11 ++++++----- runtime/polkadot/src/xcm_config.rs | 28 +++++++++++++--------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index 8cd9983c23b3..745ecea094db 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -47,7 +47,7 @@ parameter_types! { /// Our XCM location ancestry - i.e. our location within the Consensus Universe. /// /// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID. - pub Ancestry: InteriorMultiLocation = ThisNetwork::get().into(); + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -142,18 +142,18 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = LocalOriginConverter; type IsReserve = (); type IsTeleporter = TrustedTeleporters; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. - type Trader = - UsingComponents>; + type Trader = UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + // No bridges yet... type MessageExporter = (); type UniversalAliases = Nothing; } @@ -175,6 +175,7 @@ pub type LocalOriginToLocation = ( // And a usual Signed origin to be used in XCM as a corresponding AccountId32 SignedToAccountId32, ); + impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; @@ -187,7 +188,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; diff --git a/runtime/polkadot/src/xcm_config.rs b/runtime/polkadot/src/xcm_config.rs index ced0ea941f8a..2dcea130309e 100644 --- a/runtime/polkadot/src/xcm_config.rs +++ b/runtime/polkadot/src/xcm_config.rs @@ -40,11 +40,11 @@ parameter_types! { /// The location of the DOT token, from the context of this chain. Since this token is native to this /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to /// the context". - pub const DotLocation: MultiLocation = Here.into_location(); + pub const TokenLocation: MultiLocation = Here.into_location(); /// The Polkadot network ID. This is named. - pub const PolkadotNetwork: NetworkId = NetworkId::Polkadot; + pub const ThisNetwork: NetworkId = NetworkId::Polkadot; /// Our location in the universe of consensus systems. - pub const UniversalLocation: InteriorMultiLocation = X1(GlobalConsensus(NetworkId::Polkadot)); + pub const UniversalLocation: InteriorMultiLocation = X1(GlobalConsensus(ThisNetwork::get())); /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } @@ -55,18 +55,18 @@ pub type SovereignAccountOf = ( // We can convert a child parachain using the standard `AccountId` conversion. ChildParachainConvertsVia, // We can directly alias an `AccountId32` into a local account. - AccountId32Aliases, + AccountId32Aliases, ); /// Our asset transactor. This is what allows us to interest with the runtime facilities from the point of /// view of XCM-only concepts like `MultiLocation` and `MultiAsset`. /// -/// Ours is only aware of the Balances pallet, which is mapped to `DotLocation`. +/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`. pub type LocalAssetTransactor = XcmCurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + IsConcrete, // We can convert the MultiLocations with our converter above: SovereignAccountOf, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -82,7 +82,7 @@ type LocalOriginConverter = ( // A child parachain, natively expressed, has the `Parachain` origin. ChildParachainAsNative, // The AccountId32 location type can be expressed natively as a `Signed` origin. - SignedAccountId32AsNative, + SignedAccountId32AsNative, ); parameter_types! { @@ -101,12 +101,12 @@ pub type XcmRouter = ( ); parameter_types! { - pub const Polkadot: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(DotLocation::get()) }); - pub const PolkadotForStatemint: (MultiAssetFilter, MultiLocation) = (Polkadot::get(), Parachain(1000).into_location()); + pub const Dot: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); + pub const DotForStatemint: (MultiAssetFilter, MultiLocation) = (Dot::get(), Parachain(1000).into_location()); pub const MaxAssetsIntoHolding: u32 = 64; } -pub type TrustedTeleporters = (xcm_builder::Case,); +pub type TrustedTeleporters = (xcm_builder::Case,); match_type! { pub type OnlyParachains: impl Contains = { @@ -138,7 +138,7 @@ impl xcm_executor::Config for XcmConfig { type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. - type Trader = UsingComponents>; + type Trader = UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; @@ -152,8 +152,6 @@ impl xcm_executor::Config for XcmConfig { parameter_types! { pub const CouncilBodyId: BodyId = BodyId::Executive; - // We are conservative with the XCM version we advertize. - pub const AdvertisedXcmVersion: u32 = 2; } /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location @@ -167,7 +165,7 @@ pub type LocalOriginToLocation = ( CouncilBodyId, >, // And a usual Signed origin to be used in XCM as a corresponding AccountId32 - SignedToAccountId32, + SignedToAccountId32, ); impl pallet_xcm::Config for Runtime { @@ -186,5 +184,5 @@ impl pallet_xcm::Config for Runtime { type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; } From 5247473046d3c3c352a483299295c81a5d2807ca Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 9 Feb 2022 17:42:45 +0100 Subject: [PATCH 53/57] Fee handling --- runtime/kusama/src/xcm_config.rs | 1 + runtime/polkadot/src/xcm_config.rs | 1 + runtime/rococo/src/xcm_config.rs | 1 + runtime/test-runtime/src/xcm_config.rs | 1 + runtime/westend/src/xcm_config.rs | 1 + .../src/fungible/mock.rs | 1 + xcm/pallet-xcm-benchmarks/src/generic/mock.rs | 1 + xcm/pallet-xcm/src/mock.rs | 1 + xcm/xcm-builder/src/mock.rs | 10 ++++ xcm/xcm-builder/tests/mock/mod.rs | 1 + xcm/xcm-executor/src/config.rs | 5 +- xcm/xcm-executor/src/lib.rs | 51 ++++++++++++------ xcm/xcm-executor/src/traits/fee_manager.rs | 52 +++++++++++++++++++ xcm/xcm-executor/src/traits/mod.rs | 2 + 14 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 xcm/xcm-executor/src/traits/fee_manager.rs diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index 745ecea094db..ec349c31ac34 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -153,6 +153,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); // No bridges yet... type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/runtime/polkadot/src/xcm_config.rs b/runtime/polkadot/src/xcm_config.rs index 2dcea130309e..8ad61886c721 100644 --- a/runtime/polkadot/src/xcm_config.rs +++ b/runtime/polkadot/src/xcm_config.rs @@ -145,6 +145,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); // No bridges yet... type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/runtime/rococo/src/xcm_config.rs b/runtime/rococo/src/xcm_config.rs index 79c27a4ed7b1..c7de71c778d9 100644 --- a/runtime/rococo/src/xcm_config.rs +++ b/runtime/rococo/src/xcm_config.rs @@ -139,6 +139,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index dfd70159d152..a68f3f9a5159 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -104,6 +104,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = super::Xcm; type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/runtime/westend/src/xcm_config.rs b/runtime/westend/src/xcm_config.rs index fc79a86f3398..2bb915f90100 100644 --- a/runtime/westend/src/xcm_config.rs +++ b/runtime/westend/src/xcm_config.rs @@ -118,6 +118,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index e007684655f4..f139e3c0f779 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -148,6 +148,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = (); type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index ca607c2f37de..12b40dce0a29 100644 --- a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -116,6 +116,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = TestSubscriptionService; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); // No bridges yet... type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 00c409aa4c3a..7d91f50380b2 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -292,6 +292,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 66d393d0ae73..1dc5802dd8a9 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -39,6 +39,7 @@ pub use xcm::latest::prelude::*; pub use xcm_executor::{ traits::{ ConvertOrigin, ExportXcm, FilterAssetLocation, OnResponse, TransactAsset, UniversalLocation, + FeeManager, FeeReason, }, Assets, Config, }; @@ -369,6 +370,14 @@ pub type TestBarrier = ( AllowSubscriptionsFrom>, ); +pub struct TestFeeManager; +impl FeeManager for TestFeeManager { + fn is_waived(_: &Option, r: FeeReason) -> bool { + !matches!(r, FeeReason::Export(_)) + } + fn handle_fee(_: MultiAssets) {} +} + pub struct TestConfig; impl Config for TestConfig { type Call = TestCall; @@ -387,6 +396,7 @@ impl Config for TestConfig { type SubscriptionService = TestSubscriptionService; type PalletInstancesInfo = TestPalletsInfo; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = TestFeeManager; type UniversalAliases = TestUniversalAliases; type MessageExporter = TestMessageExporter; } diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index 730663a86086..b749f070a147 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -184,6 +184,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; } diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index a008b8e53c7a..09a877407aed 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -17,7 +17,7 @@ use crate::traits::{ ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, - WeightTrader, + WeightTrader, FeeManager, }; use frame_support::{ dispatch::{Dispatchable, Parameter}, @@ -80,6 +80,9 @@ pub trait Config { /// and any benchmarks should take that into account. type MaxAssetsIntoHolding: Get; + /// Configure the fees. + type FeeManager: FeeManager; + /// The method of exporting a message. type MessageExporter: ExportXcm; diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 78ddd704ab92..5fe885074f91 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -32,7 +32,7 @@ pub mod traits; use traits::{ validate_export, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, - WeightBounds, WeightTrader, + WeightBounds, WeightTrader, FeeManager, FeeReason, }; mod assets; @@ -223,6 +223,17 @@ impl XcmExecutor { } } + /// Send an XCM, charging fees from Holding as needed. + fn send(&mut self, dest: MultiLocation, msg: Xcm::<()>, reason: FeeReason) -> Result<(), XcmError> { + let (ticket, fee) = validate_send::(dest, msg)?; + if !Config::FeeManager::is_waived(&self.origin, reason) { + let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; + Config::FeeManager::handle_fee(paid.into()); + } + Config::XcmSender::deliver(ticket)?; + Ok(()) + } + /// Remove the registered error handler and return it. Do not refund its weight. fn take_error_handler(&mut self) -> Xcm { let mut r = Xcm::(vec![]); @@ -319,8 +330,7 @@ impl XcmExecutor { assets.reanchor(&dest, &context).map_err(|()| XcmError::MultiLocationFull)?; let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - let _cost = send_xcm::(dest, Xcm(message))?; - // TODO: pre-charge cost using new API. + self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; Ok(()) }, ReceiveTeleportedAsset(assets) => { @@ -401,10 +411,11 @@ impl XcmExecutor { ReportError(response_info) => { // Report the given result by sending a QueryResponse XCM to a previously given outcome // destination if one was registered. - Self::respond( + self.respond( self.origin.clone(), Response::ExecutionResult(self.error), response_info, + FeeReason::Report, ) }, DepositAsset { assets, beneficiary } => { @@ -424,8 +435,7 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest, None); let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - let _cost = send_xcm::(dest, Xcm(message))?; - // TODO: pre-charge cost using new API. + self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; Ok(()) }, InitiateReserveWithdraw { assets, reserve, xcm } => { @@ -438,8 +448,7 @@ impl XcmExecutor { ); let mut message = vec![WithdrawAsset(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - let _cost = send_xcm::(reserve, Xcm(message))?; - // TODO: pre-charge cost using new API. + self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; Ok(()) }, InitiateTeleport { assets, dest, xcm } => { @@ -453,8 +462,7 @@ impl XcmExecutor { let assets = Self::reanchored(assets, &dest, None); let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - let _cost = send_xcm::(dest, Xcm(message))?; - // TODO: pre-charge cost using new API. + self.send(dest, Xcm(message), FeeReason::InitiateTeleport)?; Ok(()) }, ReportHolding { response_info, assets } => { @@ -462,7 +470,7 @@ impl XcmExecutor { // from Holding. let assets = Self::reanchored(self.holding.min(&assets), &response_info.destination, None); - Self::respond(self.origin.clone(), Response::Assets(assets), response_info) + self.respond(self.origin.clone(), Response::Assets(assets), response_info, FeeReason::Report) }, BuyExecution { fees, weight_limit } => { // There is no need to buy any weight is `weight_limit` is `Unlimited` since it @@ -553,8 +561,7 @@ impl XcmExecutor { let querier = Self::to_querier(self.origin.clone(), &destination)?; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - let _cost = send_xcm::(destination, message)?; - // TODO: pre-charge cost using new API. + self.send(destination, message, FeeReason::QueryPallet)?; Ok(()) }, ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => { @@ -570,10 +577,11 @@ impl XcmExecutor { ensure!(minor >= min_crate_minor, XcmError::VersionIncompatible); Ok(()) }, - ReportTransactStatus(response_info) => Self::respond( + ReportTransactStatus(response_info) => self.respond( self.origin.clone(), Response::DispatchResult(self.transact_status.clone()), response_info, + FeeReason::Report, ), ClearTransactStatus => { self.transact_status = Default::default(); @@ -599,7 +607,10 @@ impl XcmExecutor { // generally have their own lanes. let (ticket, fee) = validate_export::(network, channel, destination, xcm)?; - self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; + if !Config::FeeManager::is_waived(&self.origin, FeeReason::Export(network)) { + let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; + Config::FeeManager::handle_fee(paid.into()); + } Config::MessageExporter::deliver(ticket)?; Ok(()) }, @@ -628,16 +639,22 @@ impl XcmExecutor { /// /// The `local_querier` argument is the querier (if any) specified from the *local* perspective. fn respond( + &mut self, local_querier: Option, response: Response, info: QueryResponseInfo, + fee_reason: FeeReason, ) -> Result<(), XcmError> { let querier = Self::to_querier(local_querier, &info.destination)?; let QueryResponseInfo { destination, query_id, max_weight } = info; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - let _cost = send_xcm::(destination, message)?; - // TODO: pre-charge cost using new API. + let (ticket, fee) = validate_send::(destination, message)?; + if !Config::FeeManager::is_waived(&self.origin, fee_reason) { + let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; + Config::FeeManager::handle_fee(paid.into()); + } + Config::XcmSender::deliver(ticket)?; Ok(()) } diff --git a/xcm/xcm-executor/src/traits/fee_manager.rs b/xcm/xcm-executor/src/traits/fee_manager.rs new file mode 100644 index 000000000000..9b12c186e161 --- /dev/null +++ b/xcm/xcm-executor/src/traits/fee_manager.rs @@ -0,0 +1,52 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use xcm::prelude::*; + +/// Handle stuff to do with taking fees in certain XCM instructions. +pub trait FeeManager { + /// Determine if a fee which would normally payable should be waived. + fn is_waived(origin: &Option, r: FeeReason) -> bool; + + /// Do something with the fee which has been paid. Doing nothing here silently burns the + /// fees. + fn handle_fee(fee: MultiAssets); +} + +/// Context under which a fee is paid. +pub enum FeeReason { + /// When a reporting instruction is called. + Report, + /// When the `TransferReserveAsset` instruction is called. + TransferReserveAsset, + /// When the `DepositReserveAsset` instruction is called. + DepositReserveAsset, + /// When the `InitiateReserveWithdraw` instruction is called. + InitiateReserveWithdraw, + /// When the `InitiateTeleport` instruction is called. + InitiateTeleport, + /// When the `QueryPallet` instruction is called. + QueryPallet, + /// When the `ExportMessage` instruction is called (and includes the network ID). + Export(NetworkId), +} + +impl FeeManager for () { + fn is_waived(_: &Option, _: FeeReason) -> bool { + true + } + fn handle_fee(_: MultiAssets) {} +} \ No newline at end of file diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index a8bfeea3d06d..ec3df139f315 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -24,6 +24,8 @@ mod drop_assets; pub use drop_assets::{ClaimAssets, DropAssets}; mod export; pub use export::{export_xcm, validate_export, ExportXcm}; +mod fee_manager; +pub use fee_manager::{FeeReason, FeeManager}; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; From 6ef1f0e962e6fe86b3729f1a8634f036a116003f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 14 Feb 2022 17:23:12 +0100 Subject: [PATCH 54/57] Fixes --- xcm/xcm-simulator/example/src/parachain.rs | 1 + xcm/xcm-simulator/example/src/relay_chain.rs | 1 + xcm/xcm-simulator/fuzzer/src/parachain.rs | 1 + xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 9e91398f899b..da94f067fa70 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -149,6 +149,7 @@ impl Config for XcmConfig { type AssetClaims = (); type SubscriptionService = (); type PalletInstancesInfo = (); + type FeeManager = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 3bbf956c0242..57980c8fb840 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -141,6 +141,7 @@ impl Config for XcmConfig { type AssetClaims = (); type SubscriptionService = (); type PalletInstancesInfo = (); + type FeeManager = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/xcm/xcm-simulator/fuzzer/src/parachain.rs b/xcm/xcm-simulator/fuzzer/src/parachain.rs index 3379b8a8785a..e3ae354daf30 100644 --- a/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -149,6 +149,7 @@ impl Config for XcmConfig { type AssetClaims = (); type SubscriptionService = (); type PalletInstancesInfo = (); + type FeeManager = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type MessageExporter = (); type UniversalAliases = Nothing; diff --git a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 048d858e8304..25fd22955341 100644 --- a/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -141,6 +141,7 @@ impl Config for XcmConfig { type AssetClaims = (); type SubscriptionService = (); type PalletInstancesInfo = (); + type FeeManager = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type MessageExporter = (); type UniversalAliases = Nothing; From faa7463df1f48f66e2f030fd7212b7e5a8e8d090 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 14 Feb 2022 17:41:30 +0100 Subject: [PATCH 55/57] Formatting --- runtime/kusama/src/xcm_config.rs | 3 ++- runtime/polkadot/src/xcm_config.rs | 3 ++- runtime/westend/src/lib.rs | 4 ++-- xcm/xcm-builder/src/mock.rs | 4 ++-- xcm/xcm-executor/src/config.rs | 4 ++-- xcm/xcm-executor/src/lib.rs | 23 ++++++++++++++++------ xcm/xcm-executor/src/traits/fee_manager.rs | 2 +- xcm/xcm-executor/src/traits/mod.rs | 2 +- 8 files changed, 29 insertions(+), 16 deletions(-) diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index ec349c31ac34..b7bd88054501 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -146,7 +146,8 @@ impl xcm_executor::Config for XcmConfig { type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. - type Trader = UsingComponents>; + type Trader = + UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/runtime/polkadot/src/xcm_config.rs b/runtime/polkadot/src/xcm_config.rs index 8ad61886c721..772c949fcea5 100644 --- a/runtime/polkadot/src/xcm_config.rs +++ b/runtime/polkadot/src/xcm_config.rs @@ -138,7 +138,8 @@ impl xcm_executor::Config for XcmConfig { type Barrier = Barrier; type Weigher = FixedWeightBounds; // The weight trader piggybacks on the existing transaction-fee conversion logic. - type Trader = UsingComponents>; + type Trader = + UsingComponents>; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetClaims = XcmPallet; diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index ea56034c9b15..9ba7935ee400 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1543,7 +1543,7 @@ sp_api::impl_runtime_apis! { AssetId::*, Fungibility::*, Junctions::*, MultiAsset, MultiAssets, MultiLocation, Response, }; - use xcm_config::{Westmint, WndLocation}; + use xcm_config::{Westmint, TokenLocation}; impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; @@ -1554,7 +1554,7 @@ sp_api::impl_runtime_apis! { fn worst_case_holding(_depositable_count: u32) -> MultiAssets { // Westend only knows about WND. vec![MultiAsset{ - id: Concrete(WndLocation::get()), + id: Concrete(TokenLocation::get()), fun: Fungible(1_000_000 * UNITS), }].into() } diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 1dc5802dd8a9..0d80e7fdbb28 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -38,8 +38,8 @@ pub use sp_std::{ pub use xcm::latest::prelude::*; pub use xcm_executor::{ traits::{ - ConvertOrigin, ExportXcm, FilterAssetLocation, OnResponse, TransactAsset, UniversalLocation, - FeeManager, FeeReason, + ConvertOrigin, ExportXcm, FeeManager, FeeReason, FilterAssetLocation, OnResponse, + TransactAsset, UniversalLocation, }, Assets, Config, }; diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index 09a877407aed..83f7bbb49312 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -15,9 +15,9 @@ // along with Polkadot. If not, see . use crate::traits::{ - ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, OnResponse, + ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FeeManager, FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, WeightBounds, - WeightTrader, FeeManager, + WeightTrader, }; use frame_support::{ dispatch::{Dispatchable, Parameter}, diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 5fe885074f91..3276fdb1e841 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -30,9 +30,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ - validate_export, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FilterAssetLocation, - OnResponse, ShouldExecute, TransactAsset, UniversalLocation, VersionChangeNotifier, - WeightBounds, WeightTrader, FeeManager, FeeReason, + validate_export, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FeeManager, FeeReason, + FilterAssetLocation, OnResponse, ShouldExecute, TransactAsset, UniversalLocation, + VersionChangeNotifier, WeightBounds, WeightTrader, }; mod assets; @@ -224,7 +224,12 @@ impl XcmExecutor { } /// Send an XCM, charging fees from Holding as needed. - fn send(&mut self, dest: MultiLocation, msg: Xcm::<()>, reason: FeeReason) -> Result<(), XcmError> { + fn send( + &mut self, + dest: MultiLocation, + msg: Xcm<()>, + reason: FeeReason, + ) -> Result<(), XcmError> { let (ticket, fee) = validate_send::(dest, msg)?; if !Config::FeeManager::is_waived(&self.origin, reason) { let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; @@ -470,7 +475,12 @@ impl XcmExecutor { // from Holding. let assets = Self::reanchored(self.holding.min(&assets), &response_info.destination, None); - self.respond(self.origin.clone(), Response::Assets(assets), response_info, FeeReason::Report) + self.respond( + self.origin.clone(), + Response::Assets(assets), + response_info, + FeeReason::Report, + ) }, BuyExecution { fees, weight_limit } => { // There is no need to buy any weight is `weight_limit` is `Unlimited` since it @@ -608,7 +618,8 @@ impl XcmExecutor { let (ticket, fee) = validate_export::(network, channel, destination, xcm)?; if !Config::FeeManager::is_waived(&self.origin, FeeReason::Export(network)) { - let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; + let paid = + self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; Config::FeeManager::handle_fee(paid.into()); } Config::MessageExporter::deliver(ticket)?; diff --git a/xcm/xcm-executor/src/traits/fee_manager.rs b/xcm/xcm-executor/src/traits/fee_manager.rs index 9b12c186e161..583f2242188b 100644 --- a/xcm/xcm-executor/src/traits/fee_manager.rs +++ b/xcm/xcm-executor/src/traits/fee_manager.rs @@ -49,4 +49,4 @@ impl FeeManager for () { true } fn handle_fee(_: MultiAssets) {} -} \ No newline at end of file +} diff --git a/xcm/xcm-executor/src/traits/mod.rs b/xcm/xcm-executor/src/traits/mod.rs index ec3df139f315..a08dc9b9a3f0 100644 --- a/xcm/xcm-executor/src/traits/mod.rs +++ b/xcm/xcm-executor/src/traits/mod.rs @@ -25,7 +25,7 @@ pub use drop_assets::{ClaimAssets, DropAssets}; mod export; pub use export::{export_xcm, validate_export, ExportXcm}; mod fee_manager; -pub use fee_manager::{FeeReason, FeeManager}; +pub use fee_manager::{FeeManager, FeeReason}; mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod matches_fungible; From ad5c941fc4f72e0cd6abe3d0b0219ef6e2136836 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 14 Feb 2022 17:47:48 +0100 Subject: [PATCH 56/57] Fixes --- runtime/westend/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 9ba7935ee400..d91177560bb7 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1598,7 +1598,7 @@ sp_api::impl_runtime_apis! { fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError> { let origin = Westmint::get(); - let assets: MultiAssets = (Concrete(WndLocation::get()), 1_000 * UNITS).into(); + let assets: MultiAssets = (Concrete(TokenLocation::get()), 1_000 * UNITS).into(); let ticket = MultiLocation { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } From 5ed0737c9a16205cc703b303549ffde8bcc89c01 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 14 Feb 2022 18:14:39 +0100 Subject: [PATCH 57/57] Bump --- runtime/kusama/src/lib.rs | 2 +- runtime/polkadot/src/lib.rs | 2 +- runtime/westend/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 39d7d27a008b..a9661ac77a22 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -115,7 +115,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("kusama"), impl_name: create_runtime_str!("parity-kusama"), authoring_version: 2, - spec_version: 9140, + spec_version: 9170, impl_version: 0, #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index af86b782614c..64783307255f 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -114,7 +114,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("polkadot"), impl_name: create_runtime_str!("parity-polkadot"), authoring_version: 0, - spec_version: 9140, + spec_version: 9170, impl_version: 0, #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index d91177560bb7..f0cc4cd7b553 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -111,7 +111,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westend"), impl_name: create_runtime_str!("parity-westend"), authoring_version: 2, - spec_version: 9140, + spec_version: 9170, impl_version: 0, #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS,