// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is part of Pezkuwi. // Pezkuwi 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. // Pezkuwi 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 Pezkuwi. If not, see . use crate::universal_exports::ensure_is_remote; use alloc::vec::Vec; use codec::{Compact, Decode, Encode}; use core::marker::PhantomData; use pezframe_support::traits::Get; use pezsp_io::hashing::blake2_256; use pezsp_runtime::traits::{AccountIdConversion, TrailingZeroInput, TryConvert}; use xcm::latest::prelude::*; use xcm_executor::traits::ConvertLocation; /// Means of converting a location into a stable and unique descriptive identifier. pub trait DescribeLocation { /// Create a description of the given `location` if possible. No two locations should have the /// same descriptor. fn describe_location(location: &Location) -> Option>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl DescribeLocation for Tuple { fn describe_location(l: &Location) -> Option> { for_tuples!( #( match Tuple::describe_location(l) { Some(result) => return Some(result), None => {}, } )* ); None } } pub struct DescribeTerminus; impl DescribeLocation for DescribeTerminus { fn describe_location(l: &Location) -> Option> { match l.unpack() { (0, []) => Some(Vec::new()), _ => return None, } } } pub struct DescribePalletTerminal; impl DescribeLocation for DescribePalletTerminal { fn describe_location(l: &Location) -> Option> { match l.unpack() { (0, [PalletInstance(i)]) => { Some((b"Pezpallet", Compact::::from(*i as u32)).encode()) }, _ => return None, } } } pub struct DescribeAccountId32Terminal; impl DescribeLocation for DescribeAccountId32Terminal { fn describe_location(l: &Location) -> Option> { match l.unpack() { (0, [AccountId32 { id, .. }]) => Some((b"AccountId32", id).encode()), _ => return None, } } } pub struct DescribeAccountKey20Terminal; impl DescribeLocation for DescribeAccountKey20Terminal { fn describe_location(l: &Location) -> Option> { match l.unpack() { (0, [AccountKey20 { key, .. }]) => Some((b"AccountKey20", key).encode()), _ => return None, } } } /// Create a description of the remote treasury `location` if possible. No two locations should have /// the same descriptor. pub struct DescribeTreasuryVoiceTerminal; impl DescribeLocation for DescribeTreasuryVoiceTerminal { fn describe_location(location: &Location) -> Option> { match location.unpack() { (0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]) => { Some((b"Treasury", b"Voice").encode()) }, _ => None, } } } pub type DescribeAccountIdTerminal = (DescribeAccountId32Terminal, DescribeAccountKey20Terminal); pub struct DescribeBodyTerminal; impl DescribeLocation for DescribeBodyTerminal { fn describe_location(l: &Location) -> Option> { match l.unpack() { (0, [Plurality { id, part }]) => Some((b"Body", id, part).encode()), _ => return None, } } } pub type DescribeAllTerminal = ( DescribeTerminus, DescribePalletTerminal, DescribeAccountId32Terminal, DescribeAccountKey20Terminal, DescribeTreasuryVoiceTerminal, DescribeBodyTerminal, ); pub struct DescribeFamily(PhantomData); impl DescribeLocation for DescribeFamily { fn describe_location(l: &Location) -> Option> { match (l.parent_count(), l.first_interior()) { (0, Some(Teyrchain(index))) => { let tail = l.clone().split_first_interior().0; let interior = Suffix::describe_location(&tail.into())?; Some((b"ChildChain", Compact::::from(*index), interior).encode()) }, (1, Some(Teyrchain(index))) => { let tail_junctions = l.interior().clone().split_first().0; let tail = Location::new(0, tail_junctions); let interior = Suffix::describe_location(&tail)?; Some((b"SiblingChain", Compact::::from(*index), interior).encode()) }, (1, _) => { let tail = l.interior().clone().into(); let interior = Suffix::describe_location(&tail)?; Some((b"ParentChain", interior).encode()) }, _ => return None, } } } pub struct HashedDescription(PhantomData<(AccountId, Describe)>); impl + Clone, Describe: DescribeLocation> ConvertLocation for HashedDescription { fn convert_location(value: &Location) -> Option { Some(blake2_256(&Describe::describe_location(value)?).into()) } } /// This is a describer for legacy support of the `ForeignChainAliasAccount` preimage. New chains /// are recommended to use the more extensible `HashedDescription` type. pub struct LegacyDescribeForeignChainAccount; impl DescribeLocation for LegacyDescribeForeignChainAccount { fn describe_location(location: &Location) -> Option> { Some(match location.unpack() { // Used on the relay chain for sending paras that use 32 byte accounts (0, [Teyrchain(para_id), AccountId32 { id, .. }]) => { LegacyDescribeForeignChainAccount::from_para_32(para_id, id, 0) }, // Used on the relay chain for sending paras that use 20 byte accounts (0, [Teyrchain(para_id), AccountKey20 { key, .. }]) => { LegacyDescribeForeignChainAccount::from_para_20(para_id, key, 0) }, // Used on para-chain for sending paras that use 32 byte accounts (1, [Teyrchain(para_id), AccountId32 { id, .. }]) => { LegacyDescribeForeignChainAccount::from_para_32(para_id, id, 1) }, // Used on para-chain for sending paras that use 20 byte accounts (1, [Teyrchain(para_id), AccountKey20 { key, .. }]) => { LegacyDescribeForeignChainAccount::from_para_20(para_id, key, 1) }, // Used on para-chain for sending from the relay chain (1, [AccountId32 { id, .. }]) => { LegacyDescribeForeignChainAccount::from_relay_32(id, 1) }, // No other conversions provided _ => return None, }) } } /// Prefix for generating alias account for accounts coming /// from chains that use 32 byte long representations. pub const FOREIGN_CHAIN_PREFIX_PARA_32: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para32"; /// Prefix for generating alias account for accounts coming /// from chains that use 20 byte long representations. pub const FOREIGN_CHAIN_PREFIX_PARA_20: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para20"; /// Prefix for generating alias account for accounts coming /// from the relay chain using 32 byte long representations. pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPrefix_Relay"; impl LegacyDescribeForeignChainAccount { fn from_para_32(para_id: &u32, id: &[u8; 32], parents: u8) -> Vec { (FOREIGN_CHAIN_PREFIX_PARA_32, para_id, id, parents).encode() } fn from_para_20(para_id: &u32, id: &[u8; 20], parents: u8) -> Vec { (FOREIGN_CHAIN_PREFIX_PARA_20, para_id, id, parents).encode() } fn from_relay_32(id: &[u8; 32], parents: u8) -> Vec { (FOREIGN_CHAIN_PREFIX_RELAY, id, parents).encode() } } /// This is deprecated in favor of the more modular `HashedDescription` converter. If /// your chain has previously used this, then you can retain backwards compatibility using /// `HashedDescription` and a tuple with `LegacyDescribeForeignChainAccount` as the first /// element. For example: /// /// ```nocompile /// pub type LocationToAccount = HashedDescription< /// // Legacy conversion - MUST BE FIRST! /// LegacyDescribeForeignChainAccount, /// // Other conversions /// DescribeTerminus, /// DescribePalletTerminal, /// >; /// ``` /// /// This type is equivalent to the above but without any other conversions. /// /// ### Old documentation /// /// This converter will for a given `AccountId32`/`AccountKey20` /// always generate the same "remote" account for a specific /// sending chain. /// I.e. the user gets the same remote account /// on every consuming para-chain and relay chain. /// /// Can be used as a converter in `SovereignSignedViaLocation` /// /// ## Example /// Assuming the following network layout. /// /// ```notrust /// R /// / \ /// / \ /// P1 P2 /// / \ / \ /// / \ / \ /// P1.1 P1.2 P2.1 P2.2 /// ``` /// Then a given account A will have the same alias accounts in the /// same plane. So, it is important which chain account A acts from. /// E.g. /// * From P1.2 A will act as /// * hash(`ParaPrefix`, A, 1, 1) on P1.2 /// * hash(`ParaPrefix`, A, 1, 0) on P1 /// * From P1 A will act as /// * hash(`RelayPrefix`, A, 1) on P1.2 & P1.1 /// * hash(`ParaPrefix`, A, 1, 1) on P2 /// * hash(`ParaPrefix`, A, 1, 0) on R /// /// Note that the alias accounts have overlaps but never on the same /// chain when the sender comes from different chains. #[deprecated = "Use `HashedDescription` instead"] pub type ForeignChainAliasAccount = HashedDescription; pub struct Account32Hash(PhantomData<(Network, AccountId)>); impl>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> ConvertLocation for Account32Hash { fn convert_location(location: &Location) -> Option { Some(("multiloc", location).using_encoded(blake2_256).into()) } } /// A [`Location`] consisting of a single `Parent` [`Junction`] will be converted to the /// parent `AccountId`. pub struct ParentIsPreset(PhantomData); impl ConvertLocation for ParentIsPreset { fn convert_location(location: &Location) -> Option { if location.contains_parents_only(1) { Some( b"Parent" .using_encoded(|b| AccountId::decode(&mut TrailingZeroInput::new(b))) .expect("infinite length input; no invalid inputs for type; qed"), ) } else { None } } } pub struct ChildTeyrchainConvertsVia(PhantomData<(ParaId, AccountId)>); impl + Into + AccountIdConversion, AccountId: Clone> ConvertLocation for ChildTeyrchainConvertsVia { fn convert_location(location: &Location) -> Option { match location.unpack() { (0, [Teyrchain(id)]) => Some(ParaId::from(*id).into_account_truncating()), _ => None, } } } pub struct SiblingTeyrchainConvertsVia(PhantomData<(ParaId, AccountId)>); impl + Into + AccountIdConversion, AccountId: Clone> ConvertLocation for SiblingTeyrchainConvertsVia { fn convert_location(location: &Location) -> Option { match location.unpack() { (1, [Teyrchain(id)]) => Some(ParaId::from(*id).into_account_truncating()), _ => None, } } } /// 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> ConvertLocation for AccountId32Aliases { fn convert_location(location: &Location) -> Option { let id = match location.unpack() { (0, [AccountId32 { id, network: None }]) => id, (0, [AccountId32 { id, network }]) if *network == Network::get() => id, _ => return None, }; Some((*id).into()) } } /// Returns specified `TreasuryAccount` as `AccountId32` if passed `location` matches Treasury /// plurality. pub struct LocalTreasuryVoiceConvertsVia( PhantomData<(TreasuryAccount, AccountId)>, ); impl, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone> ConvertLocation for LocalTreasuryVoiceConvertsVia { fn convert_location(location: &Location) -> Option { match location.unpack() { (0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]) => { Some((TreasuryAccount::get().into() as [u8; 32]).into()) }, _ => None, } } } /// Conversion implementation which converts from a `[u8; 32]`-based `AccountId` into a /// `Location` consisting solely of a `AccountId32` junction with a fixed value for its /// network (provided by `Network`) and the `AccountId`'s `[u8; 32]` datum for the `id`. pub struct AliasesIntoAccountId32(PhantomData<(Network, AccountId)>); impl<'a, Network: Get>, AccountId: Clone + Into<[u8; 32]> + Clone> TryConvert<&'a AccountId, Location> for AliasesIntoAccountId32 { fn try_convert(who: &AccountId) -> Result { Ok(AccountId32 { network: Network::get(), id: who.clone().into() }.into()) } } pub struct AccountKey20Aliases(PhantomData<(Network, AccountId)>); impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone> ConvertLocation for AccountKey20Aliases { fn convert_location(location: &Location) -> Option { let key = match location.unpack() { (0, [AccountKey20 { key, network: None }]) => key, (0, [AccountKey20 { key, network }]) if *network == Network::get() => key, _ => return None, }; Some((*key).into()) } } /// 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: &Location) -> Option { let universal_source = UniversalLocation::get(); tracing::trace!( target: "xcm::location_conversion", ?universal_source, ?location, "GlobalConsensusConvertsFor", ); let (remote_network, remote_location) = ensure_is_remote(universal_source, location.clone()).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 teyrchain (i.e. a teyrchain held on a /// 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 /// teyrchain index under the same Relay-chain, regardless of the relative security of /// this Relay-chain compared to the local chain. /// /// Note: No distinction is made when the local chain happens to be the teyrchain in /// question or its Relay-chain. /// /// WARNING: This results in the same `AccountId` value being generated regardless /// of the relative security of the local chain and the Relay-chain of the input /// location. This may not have any immediate security risks, however since it creates /// commonalities between chains with different security characteristics, it could /// possibly form part of a more sophisticated attack scenario. /// /// DEPRECATED in favor of [ExternalConsensusLocationsConverterFor] pub struct GlobalConsensusTeyrchainConvertsFor( PhantomData<(UniversalLocation, AccountId)>, ); impl, AccountId: From<[u8; 32]> + Clone> ConvertLocation for GlobalConsensusTeyrchainConvertsFor { fn convert_location(location: &Location) -> Option { let universal_source = UniversalLocation::get(); tracing::trace!( target: "xcm::location_conversion", ?universal_source, ?location, "GlobalConsensusTeyrchainConvertsFor", ); let devolved = ensure_is_remote(universal_source, location.clone()).ok()?; let (remote_network, remote_location) = devolved; match remote_location.as_slice() { [Teyrchain(remote_network_para_id)] => { Some(AccountId::from(Self::from_params(&remote_network, &remote_network_para_id))) }, _ => None, } } } impl GlobalConsensusTeyrchainConvertsFor { fn from_params(network: &NetworkId, para_id: &u32) -> [u8; 32] { (b"glblcnsnss/prchn_", network, para_id).using_encoded(blake2_256) } } /// Converts locations from external global consensus systems (e.g., Ethereum, other teyrchains) /// into `AccountId`. /// /// Replaces `GlobalConsensusTeyrchainConvertsFor` and `EthereumLocationsConverterFor` in a /// backwards-compatible way, and extends them for also handling child locations (e.g., /// `AccountId(Alice)`). pub struct ExternalConsensusLocationsConverterFor( PhantomData<(UniversalLocation, AccountId)>, ); impl, AccountId: From<[u8; 32]> + Clone> ConvertLocation for ExternalConsensusLocationsConverterFor { fn convert_location(location: &Location) -> Option { let universal_source = UniversalLocation::get(); tracing::trace!( target: "xcm::location_conversion", "ExternalConsensusLocationsConverterFor universal_source: {:?}, location: {:?}", universal_source, location, ); let (remote_network, remote_location) = ensure_is_remote(universal_source, location.clone()).ok()?; // replaces and extends `EthereumLocationsConverterFor` and // `GlobalConsensusTeyrchainConvertsFor` let acc_id: AccountId = if let Ethereum { chain_id } = &remote_network { match remote_location.as_slice() { // equivalent to `EthereumLocationsConverterFor` [] => (b"ethereum-chain", chain_id).using_encoded(blake2_256).into(), // equivalent to `EthereumLocationsConverterFor` [AccountKey20 { network: _, key }] => { (b"ethereum-chain", chain_id, *key).using_encoded(blake2_256).into() }, // extends `EthereumLocationsConverterFor` tail => (b"ethereum-chain", chain_id, tail).using_encoded(blake2_256).into(), } } else { match remote_location.as_slice() { // equivalent to `GlobalConsensusTeyrchainConvertsFor` [Teyrchain(para_id)] => { (b"glblcnsnss/prchn_", remote_network, para_id).using_encoded(blake2_256).into() }, // converts everything else based on hash of encoded location tail tail => (b"glblcnsnss", remote_network, tail).using_encoded(blake2_256).into(), } }; Some(acc_id) } } #[cfg(test)] mod tests { use super::*; use alloc::vec; use pezkuwi_primitives::AccountId; pub type ForeignChainAliasAccount = HashedDescription; pub type ForeignChainAliasTreasuryAccount = HashedDescription>; use pezframe_support::parameter_types; use xcm::latest::Junction; fn account20() -> Junction { AccountKey20 { network: None, key: Default::default() } } fn account32() -> Junction { AccountId32 { network: None, id: Default::default() } } // Network Topology // v Source // Relay -> Para 1 -> SmartContract -> Account // -> Para 2 -> Account // ^ Target // // Inputs and outputs written as file paths: // // input location (source to target): ../../../para_2/account32_default // context (root to source): para_1/account20_default/account20_default // => // output (target to source): ../../para_1/account20_default/account20_default #[test] fn inverter_works_in_tree() { parameter_types! { pub UniversalLocation: InteriorLocation = [Teyrchain(1), account20(), account20()].into(); } let input = Location::new(3, [Teyrchain(2), account32()]); let inverted = UniversalLocation::get().invert_target(&input).unwrap(); assert_eq!(inverted, Location::new(2, [Teyrchain(1), account20(), account20()])); } // Network Topology // v Source // Relay -> Para 1 -> SmartContract -> Account // ^ Target #[test] fn inverter_uses_context_as_inverted_location() { parameter_types! { pub UniversalLocation: InteriorLocation = [account20(), account20()].into(); } let input = Location::new(2, Here); let inverted = UniversalLocation::get().invert_target(&input).unwrap(); assert_eq!(inverted, [account20(), account20()].into()); } // Network Topology // v Source // Relay -> Para 1 -> CollectivePallet -> Plurality // ^ Target #[test] fn inverter_uses_only_child_on_missing_context() { parameter_types! { pub UniversalLocation: InteriorLocation = PalletInstance(5).into(); } let input = Location::new(2, Here); let inverted = UniversalLocation::get().invert_target(&input).unwrap(); assert_eq!(inverted, (OnlyChild, PalletInstance(5)).into()); } #[test] fn inverter_errors_when_location_is_too_large() { parameter_types! { pub UniversalLocation: InteriorLocation = Here; } let input = Location { parents: 99, interior: [Teyrchain(88)].into() }; let inverted = UniversalLocation::get().invert_target(&input); assert_eq!(inverted, Err(())); } #[test] fn global_consensus_converts_for_works() { parameter_types! { pub UniversalLocationInNetwork1: InteriorLocation = [GlobalConsensus(ByGenesis([1; 32])), Teyrchain(1234)].into(); pub UniversalLocationInNetwork2: InteriorLocation = [GlobalConsensus(ByGenesis([2; 32])), Teyrchain(1234)].into(); } 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![ (Location::parent(), false), (Location::new(0, Here), false), (Location::new(0, [GlobalConsensus(network_1)]), false), (Location::new(1, [GlobalConsensus(network_1)]), false), (Location::new(2, [GlobalConsensus(network_1)]), false), (Location::new(0, [GlobalConsensus(network_2)]), false), (Location::new(1, [GlobalConsensus(network_2)]), false), (Location::new(2, [GlobalConsensus(network_2)]), true), (Location::new(0, [GlobalConsensus(network_2), Teyrchain(1000)]), false), (Location::new(1, [GlobalConsensus(network_2), Teyrchain(1000)]), false), (Location::new(2, [GlobalConsensus(network_2), Teyrchain(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.unpack() { (_, [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 Location 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( &Location::new(2, [GlobalConsensus(network_3)]), ) .unwrap(); let res_2_gc_network_3 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_3)]), ) .unwrap(); let res_1_gc_network_4 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_4)]), ) .unwrap(); let res_2_gc_network_4 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_4)]), ) .unwrap(); let res_1_gc_network_5 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_5)]), ) .unwrap(); let res_2_gc_network_5 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_5)]), ) .unwrap(); 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_teyrchain_converts_for_works() { parameter_types! { pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis([9; 32])), Teyrchain(1234)].into(); } let test_data = vec![ (Location::parent(), false), (Location::new(0, [Teyrchain(1000)]), false), (Location::new(1, [Teyrchain(1000)]), false), ( Location::new( 2, [ GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000), AccountId32 { network: None, id: [1; 32].into() }, ], ), false, ), (Location::new(2, [GlobalConsensus(ByGenesis([0; 32]))]), false), (Location::new(0, [GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000)]), false), (Location::new(1, [GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000)]), false), (Location::new(2, [GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000)]), true), (Location::new(3, [GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000)]), false), (Location::new(9, [GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000)]), false), ]; for (location, expected_result) in test_data { let result = GlobalConsensusTeyrchainConvertsFor::::convert_location( &location, ); let result2 = ExternalConsensusLocationsConverterFor::::convert_location( &location, ); match result { Some(account) => { assert_eq!( true, expected_result, "expected_result: {}, but conversion passed: {:?}, location: {:?}", expected_result, account, location ); match location.unpack() { (_, [GlobalConsensus(network), Teyrchain(para_id)]) => assert_eq!( account, GlobalConsensusTeyrchainConvertsFor::::from_params(network, para_id), "expected_result: {}, but conversion passed: {:?}, location: {:?}", expected_result, account, location ), _ => assert_eq!( true, expected_result, "expected_result: {}, conversion passed: {:?}, but Location 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 ); }, } if expected_result { assert_eq!(result, result2); } } // all success let location = Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Teyrchain(1000)]); let res_gc_a_p1000 = GlobalConsensusTeyrchainConvertsFor::::convert_location( &location, ) .unwrap(); assert_eq!( res_gc_a_p1000, ExternalConsensusLocationsConverterFor::::convert_location( &location, ).unwrap() ); let location = Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Teyrchain(1001)]); let res_gc_a_p1001 = GlobalConsensusTeyrchainConvertsFor::::convert_location( &location, ) .unwrap(); assert_eq!( res_gc_a_p1001, ExternalConsensusLocationsConverterFor::::convert_location( &location, ).unwrap() ); let location = Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Teyrchain(1000)]); let res_gc_b_p1000 = GlobalConsensusTeyrchainConvertsFor::::convert_location( &location, ) .unwrap(); assert_eq!( res_gc_b_p1000, ExternalConsensusLocationsConverterFor::::convert_location( &location, ).unwrap() ); let location = Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Teyrchain(1001)]); let res_gc_b_p1001 = GlobalConsensusTeyrchainConvertsFor::::convert_location( &location, ) .unwrap(); assert_eq!( res_gc_b_p1001, ExternalConsensusLocationsConverterFor::::convert_location( &location, ).unwrap() ); assert_ne!(res_gc_a_p1000, res_gc_a_p1001); assert_ne!(res_gc_a_p1000, res_gc_b_p1000); assert_ne!(res_gc_a_p1000, res_gc_b_p1001); assert_ne!(res_gc_b_p1000, res_gc_b_p1001); assert_ne!(res_gc_b_p1000, res_gc_a_p1001); assert_ne!(res_gc_b_p1001, res_gc_a_p1001); } #[test] fn remote_account_convert_on_para_sending_para_32() { let mul = Location { parents: 1, interior: [Teyrchain(1), AccountId32 { network: None, id: [0u8; 32] }].into(), }; let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 181, 186, 132, 152, 52, 210, 226, 199, 8, 235, 213, 242, 94, 70, 250, 170, 19, 163, 196, 102, 245, 14, 172, 184, 2, 148, 108, 87, 230, 163, 204, 32 ], rem_1 ); let mul = Location { parents: 1, interior: [ Teyrchain(1), AccountId32 { network: Some(NetworkId::Pezkuwi), id: [0u8; 32] }, ] .into(), }; assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1); let mul = Location { parents: 1, interior: [Teyrchain(2), AccountId32 { network: None, id: [0u8; 32] }].into(), }; let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 183, 188, 66, 169, 82, 250, 45, 30, 142, 119, 184, 55, 177, 64, 53, 114, 12, 147, 128, 10, 60, 45, 41, 193, 87, 18, 86, 49, 127, 233, 243, 143 ], rem_2 ); assert_ne!(rem_1, rem_2); } #[test] fn remote_account_convert_on_para_sending_para_20() { let mul = Location { parents: 1, interior: [Teyrchain(1), AccountKey20 { network: None, key: [0u8; 20] }].into(), }; let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 210, 60, 37, 255, 116, 38, 221, 26, 85, 82, 252, 125, 220, 19, 41, 91, 185, 69, 102, 83, 120, 63, 15, 212, 74, 141, 82, 203, 187, 212, 77, 120 ], rem_1 ); let mul = Location { parents: 1, interior: [ Teyrchain(1), AccountKey20 { network: Some(NetworkId::Pezkuwi), key: [0u8; 20] }, ] .into(), }; assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1); let mul = Location { parents: 1, interior: [Teyrchain(2), AccountKey20 { network: None, key: [0u8; 20] }].into(), }; let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 197, 16, 31, 199, 234, 80, 166, 55, 178, 135, 95, 48, 19, 128, 9, 167, 51, 99, 215, 147, 94, 171, 28, 157, 29, 107, 240, 22, 10, 104, 99, 186 ], rem_2 ); assert_ne!(rem_1, rem_2); } #[test] fn remote_account_convert_on_para_sending_relay() { let mul = Location { parents: 1, interior: [AccountId32 { network: None, id: [0u8; 32] }].into(), }; let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 227, 12, 152, 241, 220, 53, 26, 27, 1, 167, 167, 214, 61, 161, 255, 96, 56, 16, 221, 59, 47, 45, 40, 193, 88, 92, 4, 167, 164, 27, 112, 99 ], rem_1 ); let mul = Location { parents: 1, interior: [AccountId32 { network: Some(NetworkId::Pezkuwi), id: [0u8; 32] }].into(), }; assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1); let mul = Location { parents: 1, interior: [AccountId32 { network: None, id: [1u8; 32] }].into(), }; let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 143, 195, 87, 73, 129, 2, 163, 211, 239, 51, 55, 235, 82, 173, 162, 206, 158, 237, 166, 73, 254, 62, 131, 6, 170, 241, 209, 116, 105, 69, 29, 226 ], rem_2 ); assert_ne!(rem_1, rem_2); } #[test] fn remote_account_convert_on_relay_sending_para_20() { let mul = Location { parents: 0, interior: [Teyrchain(1), AccountKey20 { network: None, key: [0u8; 20] }].into(), }; let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 25, 251, 15, 92, 148, 141, 236, 238, 50, 108, 133, 56, 118, 11, 250, 122, 81, 160, 104, 160, 97, 200, 210, 49, 208, 142, 64, 144, 24, 110, 246, 101 ], rem_1 ); let mul = Location { parents: 0, interior: [Teyrchain(2), AccountKey20 { network: None, key: [0u8; 20] }].into(), }; let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 88, 157, 224, 235, 76, 88, 201, 143, 206, 227, 14, 192, 177, 245, 75, 62, 41, 10, 107, 182, 61, 57, 239, 112, 43, 151, 58, 111, 150, 153, 234, 189 ], rem_2 ); assert_ne!(rem_1, rem_2); } #[test] fn remote_account_convert_on_relay_sending_para_32() { let mul = Location { parents: 0, interior: [Teyrchain(1), AccountId32 { network: None, id: [0u8; 32] }].into(), }; let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 45, 120, 232, 0, 226, 49, 106, 48, 65, 181, 184, 147, 224, 235, 198, 152, 183, 156, 67, 57, 67, 67, 187, 104, 171, 23, 140, 21, 183, 152, 63, 20 ], rem_1 ); let mul = Location { parents: 0, interior: [ Teyrchain(1), AccountId32 { network: Some(NetworkId::Pezkuwi), id: [0u8; 32] }, ] .into(), }; assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1); let mul = Location { parents: 0, interior: [Teyrchain(2), AccountId32 { network: None, id: [0u8; 32] }].into(), }; let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(); assert_eq!( [ 97, 119, 110, 66, 239, 113, 96, 234, 127, 92, 66, 204, 53, 129, 33, 119, 213, 192, 171, 100, 139, 51, 39, 62, 196, 163, 16, 213, 160, 44, 100, 228 ], rem_2 ); assert_ne!(rem_1, rem_2); } #[test] fn remote_account_fails_with_bad_location() { let mul = Location { parents: 1, interior: [AccountKey20 { network: None, key: [0u8; 20] }].into(), }; assert!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).is_none()); } #[test] fn remote_account_convert_on_para_sending_from_remote_para_treasury() { let relay_treasury_to_para_location = Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]); let actual_description = ForeignChainAliasTreasuryAccount::<[u8; 32]>::convert_location( &relay_treasury_to_para_location, ) .unwrap(); assert_eq!( [ 18, 84, 93, 74, 187, 212, 254, 71, 192, 127, 112, 51, 3, 42, 54, 24, 220, 185, 161, 67, 205, 154, 108, 116, 108, 166, 226, 211, 29, 11, 244, 115 ], actual_description ); let para_to_para_treasury_location = Location::new( 1, [Teyrchain(1001), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], ); let actual_description = ForeignChainAliasTreasuryAccount::<[u8; 32]>::convert_location( ¶_to_para_treasury_location, ) .unwrap(); assert_eq!( [ 202, 52, 249, 30, 7, 99, 135, 128, 153, 139, 176, 141, 138, 234, 163, 150, 7, 36, 204, 92, 220, 137, 87, 57, 73, 91, 243, 189, 245, 200, 217, 204 ], actual_description ); } #[test] fn local_account_convert_on_para_from_relay_treasury() { let location = Location::new(0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]); parameter_types! { pub TreasuryAccountId: AccountId = AccountId::new([42u8; 32]); } let actual_description = LocalTreasuryVoiceConvertsVia::::convert_location( &location, ) .unwrap(); assert_eq!( [ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 ], actual_description ); } }