diff --git a/xcm/src/v3/multiasset.rs b/xcm/src/v3/multiasset.rs index a7a658f06017..a4900a71539a 100644 --- a/xcm/src/v3/multiasset.rs +++ b/xcm/src/v3/multiasset.rs @@ -406,7 +406,7 @@ pub struct MultiAsset { /// The overall asset identity (aka *class*, in the case of a non-fungible). pub id: AssetId, /// The fungibility of the asset, which contains either the amount (in the case of a fungible - /// asset) or the *instance ID`, the secondary asset identifier. + /// asset) or the *instance ID*, the secondary asset identifier. pub fun: Fungibility, } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 984ace84dc69..e3f910409638 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -33,8 +33,9 @@ pub use location_conversion::{ Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32, ChildParachainConvertsVia, DescribeAccountId32Terminal, DescribeAccountIdTerminal, DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeFamily, DescribeLocation, - DescribePalletTerminal, DescribeTerminus, GlobalConsensusParachainConvertsFor, - HashedDescription, ParentIsPreset, SiblingParachainConvertsVia, + DescribePalletTerminal, DescribeTerminus, GlobalConsensusConvertsFor, + GlobalConsensusParachainConvertsFor, HashedDescription, ParentIsPreset, + SiblingParachainConvertsVia, }; mod origin_conversion; diff --git a/xcm/xcm-builder/src/location_conversion.rs b/xcm/xcm-builder/src/location_conversion.rs index 3a95884328a1..ccc3cc040e61 100644 --- a/xcm/xcm-builder/src/location_conversion.rs +++ b/xcm/xcm-builder/src/location_conversion.rs @@ -345,6 +345,42 @@ impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> } } +/// Converts a location which is a top-level relay chain (which provides its own consensus) into a 32-byte `AccountId`. +/// +/// This will always result in the *same account ID* being returned for the same Relay-chain, regardless of the relative security of +/// this Relay-chain compared to the local chain. +/// +/// Note: No distinction is made between the cases when the given `UniversalLocation` lies within +/// the same consensus system (i.e. is itself or a parent) and when it is a foreign consensus +/// system. +pub struct GlobalConsensusConvertsFor( + PhantomData<(UniversalLocation, AccountId)>, +); +impl, AccountId: From<[u8; 32]> + Clone> + ConvertLocation for GlobalConsensusConvertsFor +{ + fn convert_location(location: &MultiLocation) -> Option { + let universal_source = UniversalLocation::get(); + log::trace!( + target: "xcm::location_conversion", + "GlobalConsensusConvertsFor universal_source: {:?}, location: {:?}", + universal_source, location, + ); + let (remote_network, remote_location) = + ensure_is_remote(universal_source, *location).ok()?; + + match remote_location { + Here => Some(AccountId::from(Self::from_params(&remote_network))), + _ => None, + } + } +} +impl GlobalConsensusConvertsFor { + fn from_params(network: &NetworkId) -> [u8; 32] { + (b"glblcnsnss_", network).using_encoded(blake2_256) + } +} + /// Converts a location which is a top-level parachain (i.e. a parachain held on a /// Relay-chain which provides its own consensus) into a 32-byte `AccountId`. /// @@ -473,6 +509,105 @@ mod tests { assert_eq!(inverted, Err(())); } + #[test] + fn global_consensus_converts_for_works() { + parameter_types! { + pub UniversalLocationInNetwork1: InteriorMultiLocation = X2(GlobalConsensus(ByGenesis([1; 32])), Parachain(1234)); + pub UniversalLocationInNetwork2: InteriorMultiLocation = X2(GlobalConsensus(ByGenesis([2; 32])), Parachain(1234)); + } + let network_1 = UniversalLocationInNetwork1::get().global_consensus().expect("NetworkId"); + let network_2 = UniversalLocationInNetwork2::get().global_consensus().expect("NetworkId"); + let network_3 = ByGenesis([3; 32]); + let network_4 = ByGenesis([4; 32]); + let network_5 = ByGenesis([5; 32]); + + let test_data = vec![ + (MultiLocation::parent(), false), + (MultiLocation::new(0, Here), false), + (MultiLocation::new(0, X1(GlobalConsensus(network_1))), false), + (MultiLocation::new(1, X1(GlobalConsensus(network_1))), false), + (MultiLocation::new(2, X1(GlobalConsensus(network_1))), false), + (MultiLocation::new(0, X1(GlobalConsensus(network_2))), false), + (MultiLocation::new(1, X1(GlobalConsensus(network_2))), false), + (MultiLocation::new(2, X1(GlobalConsensus(network_2))), true), + (MultiLocation::new(0, X2(GlobalConsensus(network_2), Parachain(1000))), false), + (MultiLocation::new(1, X2(GlobalConsensus(network_2), Parachain(1000))), false), + (MultiLocation::new(2, X2(GlobalConsensus(network_2), Parachain(1000))), false), + ]; + + for (location, expected_result) in test_data { + let result = + GlobalConsensusConvertsFor::::convert_location( + &location, + ); + match result { + Some(account) => { + assert_eq!( + true, expected_result, + "expected_result: {}, but conversion passed: {:?}, location: {:?}", + expected_result, account, location + ); + match &location { + MultiLocation { interior: X1(GlobalConsensus(network)), .. } => + assert_eq!( + account, + GlobalConsensusConvertsFor::::from_params(network), + "expected_result: {}, but conversion passed: {:?}, location: {:?}", expected_result, account, location + ), + _ => panic!("expected_result: {}, conversion passed: {:?}, but MultiLocation does not match expected pattern, location: {:?}", expected_result, account, location) + } + }, + None => { + assert_eq!( + false, expected_result, + "expected_result: {} - but conversion failed, location: {:?}", + expected_result, location + ); + }, + } + } + + // all success + let res_1_gc_network_3 = + GlobalConsensusConvertsFor::::convert_location( + &MultiLocation::new(2, X1(GlobalConsensus(network_3))), + ) + .expect("conversion is ok"); + let res_2_gc_network_3 = + GlobalConsensusConvertsFor::::convert_location( + &MultiLocation::new(2, X1(GlobalConsensus(network_3))), + ) + .expect("conversion is ok"); + let res_1_gc_network_4 = + GlobalConsensusConvertsFor::::convert_location( + &MultiLocation::new(2, X1(GlobalConsensus(network_4))), + ) + .expect("conversion is ok"); + let res_2_gc_network_4 = + GlobalConsensusConvertsFor::::convert_location( + &MultiLocation::new(2, X1(GlobalConsensus(network_4))), + ) + .expect("conversion is ok"); + let res_1_gc_network_5 = + GlobalConsensusConvertsFor::::convert_location( + &MultiLocation::new(2, X1(GlobalConsensus(network_5))), + ) + .expect("conversion is ok"); + let res_2_gc_network_5 = + GlobalConsensusConvertsFor::::convert_location( + &MultiLocation::new(2, X1(GlobalConsensus(network_5))), + ) + .expect("conversion is ok"); + + assert_ne!(res_1_gc_network_3, res_1_gc_network_4); + assert_ne!(res_1_gc_network_4, res_1_gc_network_5); + assert_ne!(res_1_gc_network_3, res_1_gc_network_5); + + assert_eq!(res_1_gc_network_3, res_2_gc_network_3); + assert_eq!(res_1_gc_network_4, res_2_gc_network_4); + assert_eq!(res_1_gc_network_5, res_2_gc_network_5); + } + #[test] fn global_consensus_parachain_converts_for_works() { parameter_types! {