referrerpolicy=no-referrer-when-downgrade

staging_xcm_builder/
location_conversion.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17use crate::universal_exports::ensure_is_remote;
18use alloc::vec::Vec;
19use codec::{Compact, Decode, Encode};
20use core::marker::PhantomData;
21use frame_support::traits::Get;
22use sp_io::hashing::blake2_256;
23use sp_runtime::traits::{AccountIdConversion, TrailingZeroInput, TryConvert};
24use xcm::latest::prelude::*;
25use xcm_executor::traits::ConvertLocation;
26
27/// Means of converting a location into a stable and unique descriptive identifier.
28pub trait DescribeLocation {
29	/// Create a description of the given `location` if possible. No two locations should have the
30	/// same descriptor.
31	fn describe_location(location: &Location) -> Option<Vec<u8>>;
32}
33
34#[impl_trait_for_tuples::impl_for_tuples(30)]
35impl DescribeLocation for Tuple {
36	fn describe_location(l: &Location) -> Option<Vec<u8>> {
37		for_tuples!( #(
38			match Tuple::describe_location(l) {
39				Some(result) => return Some(result),
40				None => {},
41			}
42		)* );
43		None
44	}
45}
46
47pub struct DescribeTerminus;
48impl DescribeLocation for DescribeTerminus {
49	fn describe_location(l: &Location) -> Option<Vec<u8>> {
50		match l.unpack() {
51			(0, []) => Some(Vec::new()),
52			_ => return None,
53		}
54	}
55}
56
57pub struct DescribePalletTerminal;
58impl DescribeLocation for DescribePalletTerminal {
59	fn describe_location(l: &Location) -> Option<Vec<u8>> {
60		match l.unpack() {
61			(0, [PalletInstance(i)]) => Some((b"Pallet", Compact::<u32>::from(*i as u32)).encode()),
62			_ => return None,
63		}
64	}
65}
66
67pub struct DescribeAccountId32Terminal;
68impl DescribeLocation for DescribeAccountId32Terminal {
69	fn describe_location(l: &Location) -> Option<Vec<u8>> {
70		match l.unpack() {
71			(0, [AccountId32 { id, .. }]) => Some((b"AccountId32", id).encode()),
72			_ => return None,
73		}
74	}
75}
76
77pub struct DescribeAccountKey20Terminal;
78impl DescribeLocation for DescribeAccountKey20Terminal {
79	fn describe_location(l: &Location) -> Option<Vec<u8>> {
80		match l.unpack() {
81			(0, [AccountKey20 { key, .. }]) => Some((b"AccountKey20", key).encode()),
82			_ => return None,
83		}
84	}
85}
86
87/// Create a description of the remote treasury `location` if possible. No two locations should have
88/// the same descriptor.
89pub struct DescribeTreasuryVoiceTerminal;
90
91impl DescribeLocation for DescribeTreasuryVoiceTerminal {
92	fn describe_location(location: &Location) -> Option<Vec<u8>> {
93		match location.unpack() {
94			(0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]) => {
95				Some((b"Treasury", b"Voice").encode())
96			},
97			_ => None,
98		}
99	}
100}
101
102pub type DescribeAccountIdTerminal = (DescribeAccountId32Terminal, DescribeAccountKey20Terminal);
103
104pub struct DescribeBodyTerminal;
105impl DescribeLocation for DescribeBodyTerminal {
106	fn describe_location(l: &Location) -> Option<Vec<u8>> {
107		match l.unpack() {
108			(0, [Plurality { id, part }]) => Some((b"Body", id, part).encode()),
109			_ => return None,
110		}
111	}
112}
113
114pub type DescribeAllTerminal = (
115	DescribeTerminus,
116	DescribePalletTerminal,
117	DescribeAccountId32Terminal,
118	DescribeAccountKey20Terminal,
119	DescribeTreasuryVoiceTerminal,
120	DescribeBodyTerminal,
121);
122
123pub struct DescribeFamily<DescribeInterior>(PhantomData<DescribeInterior>);
124impl<Suffix: DescribeLocation> DescribeLocation for DescribeFamily<Suffix> {
125	fn describe_location(l: &Location) -> Option<Vec<u8>> {
126		match (l.parent_count(), l.first_interior()) {
127			(0, Some(Parachain(index))) => {
128				let tail = l.clone().split_first_interior().0;
129				let interior = Suffix::describe_location(&tail.into())?;
130				Some((b"ChildChain", Compact::<u32>::from(*index), interior).encode())
131			},
132			(1, Some(Parachain(index))) => {
133				let tail_junctions = l.interior().clone().split_first().0;
134				let tail = Location::new(0, tail_junctions);
135				let interior = Suffix::describe_location(&tail)?;
136				Some((b"SiblingChain", Compact::<u32>::from(*index), interior).encode())
137			},
138			(1, _) => {
139				let tail = l.interior().clone().into();
140				let interior = Suffix::describe_location(&tail)?;
141				Some((b"ParentChain", interior).encode())
142			},
143			_ => return None,
144		}
145	}
146}
147
148pub struct HashedDescription<AccountId, Describe>(PhantomData<(AccountId, Describe)>);
149impl<AccountId: From<[u8; 32]> + Clone, Describe: DescribeLocation> ConvertLocation<AccountId>
150	for HashedDescription<AccountId, Describe>
151{
152	fn convert_location(value: &Location) -> Option<AccountId> {
153		Some(blake2_256(&Describe::describe_location(value)?).into())
154	}
155}
156
157/// This is a describer for legacy support of the `ForeignChainAliasAccount` preimage. New chains
158/// are recommended to use the more extensible `HashedDescription` type.
159///
160/// Kept for chains using `HashedDescription<AccountId, LegacyDescribeForeignChainAccount>`.
161#[allow(dead_code)]
162pub struct LegacyDescribeForeignChainAccount;
163impl DescribeLocation for LegacyDescribeForeignChainAccount {
164	fn describe_location(location: &Location) -> Option<Vec<u8>> {
165		Some(match location.unpack() {
166			// Used on the relay chain for sending paras that use 32 byte accounts
167			(0, [Parachain(para_id), AccountId32 { id, .. }]) => {
168				LegacyDescribeForeignChainAccount::from_para_32(para_id, id, 0)
169			},
170
171			// Used on the relay chain for sending paras that use 20 byte accounts
172			(0, [Parachain(para_id), AccountKey20 { key, .. }]) => {
173				LegacyDescribeForeignChainAccount::from_para_20(para_id, key, 0)
174			},
175
176			// Used on para-chain for sending paras that use 32 byte accounts
177			(1, [Parachain(para_id), AccountId32 { id, .. }]) => {
178				LegacyDescribeForeignChainAccount::from_para_32(para_id, id, 1)
179			},
180
181			// Used on para-chain for sending paras that use 20 byte accounts
182			(1, [Parachain(para_id), AccountKey20 { key, .. }]) => {
183				LegacyDescribeForeignChainAccount::from_para_20(para_id, key, 1)
184			},
185
186			// Used on para-chain for sending from the relay chain
187			(1, [AccountId32 { id, .. }]) => {
188				LegacyDescribeForeignChainAccount::from_relay_32(id, 1)
189			},
190
191			// No other conversions provided
192			_ => return None,
193		})
194	}
195}
196
197/// Prefix for generating alias account for accounts coming
198/// from chains that use 32 byte long representations.
199#[allow(dead_code)]
200pub const FOREIGN_CHAIN_PREFIX_PARA_32: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para32";
201
202/// Prefix for generating alias account for accounts coming
203/// from chains that use 20 byte long representations.
204#[allow(dead_code)]
205pub const FOREIGN_CHAIN_PREFIX_PARA_20: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para20";
206
207/// Prefix for generating alias account for accounts coming
208/// from the relay chain using 32 byte long representations.
209#[allow(dead_code)]
210pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPrefix_Relay";
211
212#[allow(dead_code)]
213impl LegacyDescribeForeignChainAccount {
214	fn from_para_32(para_id: &u32, id: &[u8; 32], parents: u8) -> Vec<u8> {
215		(FOREIGN_CHAIN_PREFIX_PARA_32, para_id, id, parents).encode()
216	}
217
218	fn from_para_20(para_id: &u32, id: &[u8; 20], parents: u8) -> Vec<u8> {
219		(FOREIGN_CHAIN_PREFIX_PARA_20, para_id, id, parents).encode()
220	}
221
222	fn from_relay_32(id: &[u8; 32], parents: u8) -> Vec<u8> {
223		(FOREIGN_CHAIN_PREFIX_RELAY, id, parents).encode()
224	}
225}
226
227pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>);
228impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
229	ConvertLocation<AccountId> for Account32Hash<Network, AccountId>
230{
231	fn convert_location(location: &Location) -> Option<AccountId> {
232		Some(("multiloc", location).using_encoded(blake2_256).into())
233	}
234}
235
236/// A [`Location`] consisting of a single `Parent` [`Junction`] will be converted to the
237/// parent `AccountId`.
238pub struct ParentIsPreset<AccountId>(PhantomData<AccountId>);
239impl<AccountId: Decode + Eq + Clone> ConvertLocation<AccountId> for ParentIsPreset<AccountId> {
240	fn convert_location(location: &Location) -> Option<AccountId> {
241		if location.contains_parents_only(1) {
242			Some(
243				b"Parent"
244					.using_encoded(|b| AccountId::decode(&mut TrailingZeroInput::new(b)))
245					.expect("infinite length input; no invalid inputs for type; qed"),
246			)
247		} else {
248			None
249		}
250	}
251}
252
253pub struct ChildParachainConvertsVia<ParaId, AccountId>(PhantomData<(ParaId, AccountId)>);
254impl<ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>, AccountId: Clone>
255	ConvertLocation<AccountId> for ChildParachainConvertsVia<ParaId, AccountId>
256{
257	fn convert_location(location: &Location) -> Option<AccountId> {
258		match location.unpack() {
259			(0, [Parachain(id)]) => Some(ParaId::from(*id).into_account_truncating()),
260			_ => None,
261		}
262	}
263}
264
265pub struct SiblingParachainConvertsVia<ParaId, AccountId>(PhantomData<(ParaId, AccountId)>);
266impl<ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>, AccountId: Clone>
267	ConvertLocation<AccountId> for SiblingParachainConvertsVia<ParaId, AccountId>
268{
269	fn convert_location(location: &Location) -> Option<AccountId> {
270		match location.unpack() {
271			(1, [Parachain(id)]) => Some(ParaId::from(*id).into_account_truncating()),
272			_ => None,
273		}
274	}
275}
276
277/// Extracts the `AccountId32` from the passed `location` if the network matches.
278pub struct AccountId32Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
279impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
280	ConvertLocation<AccountId> for AccountId32Aliases<Network, AccountId>
281{
282	fn convert_location(location: &Location) -> Option<AccountId> {
283		let id = match location.unpack() {
284			(0, [AccountId32 { id, network: None }]) => id,
285			(0, [AccountId32 { id, network }]) if *network == Network::get() => id,
286			_ => return None,
287		};
288		Some((*id).into())
289	}
290}
291
292/// Returns specified `TreasuryAccount` as `AccountId32` if passed `location` matches Treasury
293/// plurality.
294pub struct LocalTreasuryVoiceConvertsVia<TreasuryAccount, AccountId>(
295	PhantomData<(TreasuryAccount, AccountId)>,
296);
297impl<TreasuryAccount: Get<AccountId>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
298	ConvertLocation<AccountId> for LocalTreasuryVoiceConvertsVia<TreasuryAccount, AccountId>
299{
300	fn convert_location(location: &Location) -> Option<AccountId> {
301		match location.unpack() {
302			(0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]) => {
303				Some((TreasuryAccount::get().into() as [u8; 32]).into())
304			},
305			_ => None,
306		}
307	}
308}
309
310/// Conversion implementation which converts from a `[u8; 32]`-based `AccountId` into a
311/// `Location` consisting solely of a `AccountId32` junction with a fixed value for its
312/// network (provided by `Network`) and the `AccountId`'s `[u8; 32]` datum for the `id`.
313pub struct AliasesIntoAccountId32<Network, AccountId>(PhantomData<(Network, AccountId)>);
314impl<'a, Network: Get<Option<NetworkId>>, AccountId: Clone + Into<[u8; 32]> + Clone>
315	TryConvert<&'a AccountId, Location> for AliasesIntoAccountId32<Network, AccountId>
316{
317	fn try_convert(who: &AccountId) -> Result<Location, &AccountId> {
318		Ok(AccountId32 { network: Network::get(), id: who.clone().into() }.into())
319	}
320}
321
322pub struct AccountKey20Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
323impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone>
324	ConvertLocation<AccountId> for AccountKey20Aliases<Network, AccountId>
325{
326	fn convert_location(location: &Location) -> Option<AccountId> {
327		let key = match location.unpack() {
328			(0, [AccountKey20 { key, network: None }]) => key,
329			(0, [AccountKey20 { key, network }]) if *network == Network::get() => key,
330			_ => return None,
331		};
332		Some((*key).into())
333	}
334}
335
336/// Converts a location which is a top-level relay chain (which provides its own consensus) into a
337/// 32-byte `AccountId`.
338///
339/// This will always result in the *same account ID* being returned for the same Relay-chain,
340/// regardless of the relative security of this Relay-chain compared to the local chain.
341///
342/// Note: No distinction is made between the cases when the given `UniversalLocation` lies within
343/// the same consensus system (i.e. is itself or a parent) and when it is a foreign consensus
344/// system.
345pub struct GlobalConsensusConvertsFor<UniversalLocation, AccountId>(
346	PhantomData<(UniversalLocation, AccountId)>,
347);
348impl<UniversalLocation: Get<InteriorLocation>, AccountId: From<[u8; 32]> + Clone>
349	ConvertLocation<AccountId> for GlobalConsensusConvertsFor<UniversalLocation, AccountId>
350{
351	fn convert_location(location: &Location) -> Option<AccountId> {
352		let universal_source = UniversalLocation::get();
353		tracing::trace!(
354			target: "xcm::location_conversion",
355			?universal_source, ?location,
356			"GlobalConsensusConvertsFor",
357		);
358		let (remote_network, remote_location) =
359			ensure_is_remote(universal_source, location.clone()).ok()?;
360
361		match remote_location {
362			Here => Some(AccountId::from(Self::from_params(&remote_network))),
363			_ => None,
364		}
365	}
366}
367impl<UniversalLocation, AccountId> GlobalConsensusConvertsFor<UniversalLocation, AccountId> {
368	fn from_params(network: &NetworkId) -> [u8; 32] {
369		(b"glblcnsnss_", network).using_encoded(blake2_256)
370	}
371}
372
373/// Converts a location which is a top-level parachain (i.e. a parachain held on a
374/// Relay-chain which provides its own consensus) into a 32-byte `AccountId`.
375///
376/// This will always result in the *same account ID* being returned for the same
377/// parachain index under the same Relay-chain, regardless of the relative security of
378/// this Relay-chain compared to the local chain.
379///
380/// Note: No distinction is made when the local chain happens to be the parachain in
381/// question or its Relay-chain.
382///
383/// WARNING: This results in the same `AccountId` value being generated regardless
384/// of the relative security of the local chain and the Relay-chain of the input
385/// location. This may not have any immediate security risks, however since it creates
386/// commonalities between chains with different security characteristics, it could
387/// possibly form part of a more sophisticated attack scenario.
388///
389/// DEPRECATED in favor of [ExternalConsensusLocationsConverterFor]
390pub struct GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>(
391	PhantomData<(UniversalLocation, AccountId)>,
392);
393impl<UniversalLocation: Get<InteriorLocation>, AccountId: From<[u8; 32]> + Clone>
394	ConvertLocation<AccountId> for GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>
395{
396	fn convert_location(location: &Location) -> Option<AccountId> {
397		let universal_source = UniversalLocation::get();
398		tracing::trace!(
399			target: "xcm::location_conversion",
400			?universal_source, ?location,
401			"GlobalConsensusParachainConvertsFor",
402		);
403		let devolved = ensure_is_remote(universal_source, location.clone()).ok()?;
404		let (remote_network, remote_location) = devolved;
405
406		match remote_location.as_slice() {
407			[Parachain(remote_network_para_id)] => {
408				Some(AccountId::from(Self::from_params(&remote_network, &remote_network_para_id)))
409			},
410			_ => None,
411		}
412	}
413}
414impl<UniversalLocation, AccountId>
415	GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>
416{
417	fn from_params(network: &NetworkId, para_id: &u32) -> [u8; 32] {
418		(b"glblcnsnss/prchn_", network, para_id).using_encoded(blake2_256)
419	}
420}
421
422/// Converts locations from external global consensus systems (e.g., Ethereum, other parachains)
423/// into `AccountId`.
424///
425/// Replaces `GlobalConsensusParachainConvertsFor` and `EthereumLocationsConverterFor` in a
426/// backwards-compatible way, and extends them for also handling child locations (e.g.,
427/// `AccountId(Alice)`).
428pub struct ExternalConsensusLocationsConverterFor<UniversalLocation, AccountId>(
429	PhantomData<(UniversalLocation, AccountId)>,
430);
431
432impl<UniversalLocation: Get<InteriorLocation>, AccountId: From<[u8; 32]> + Clone>
433	ConvertLocation<AccountId>
434	for ExternalConsensusLocationsConverterFor<UniversalLocation, AccountId>
435{
436	fn convert_location(location: &Location) -> Option<AccountId> {
437		let universal_source = UniversalLocation::get();
438		tracing::trace!(
439			target: "xcm::location_conversion",
440			"ExternalConsensusLocationsConverterFor universal_source: {:?}, location: {:?}",
441			universal_source, location,
442		);
443		let (remote_network, remote_location) =
444			ensure_is_remote(universal_source, location.clone()).ok()?;
445
446		// replaces and extends `EthereumLocationsConverterFor` and
447		// `GlobalConsensusParachainConvertsFor`
448		let acc_id: AccountId = if let Ethereum { chain_id } = &remote_network {
449			match remote_location.as_slice() {
450				// equivalent to `EthereumLocationsConverterFor`
451				[] => (b"ethereum-chain", chain_id).using_encoded(blake2_256).into(),
452				// equivalent to `EthereumLocationsConverterFor`
453				[AccountKey20 { network: _, key }] => {
454					(b"ethereum-chain", chain_id, *key).using_encoded(blake2_256).into()
455				},
456				// extends `EthereumLocationsConverterFor`
457				tail => (b"ethereum-chain", chain_id, tail).using_encoded(blake2_256).into(),
458			}
459		} else {
460			match remote_location.as_slice() {
461				// equivalent to `GlobalConsensusParachainConvertsFor`
462				[Parachain(para_id)] => {
463					(b"glblcnsnss/prchn_", remote_network, para_id).using_encoded(blake2_256).into()
464				},
465				// converts everything else based on hash of encoded location tail
466				tail => (b"glblcnsnss", remote_network, tail).using_encoded(blake2_256).into(),
467			}
468		};
469		Some(acc_id)
470	}
471}
472
473#[cfg(test)]
474mod tests {
475	use super::*;
476	use alloc::vec;
477	use polkadot_primitives::AccountId;
478
479	pub type ForeignChainAliasAccount<AccountId> =
480		HashedDescription<AccountId, LegacyDescribeForeignChainAccount>;
481
482	pub type ForeignChainAliasTreasuryAccount<AccountId> =
483		HashedDescription<AccountId, DescribeFamily<DescribeTreasuryVoiceTerminal>>;
484
485	use frame_support::parameter_types;
486	use xcm::latest::Junction;
487
488	fn account20() -> Junction {
489		AccountKey20 { network: None, key: Default::default() }
490	}
491
492	fn account32() -> Junction {
493		AccountId32 { network: None, id: Default::default() }
494	}
495
496	// Network Topology
497	//                                     v Source
498	// Relay -> Para 1 -> SmartContract -> Account
499	//       -> Para 2 -> Account
500	//                    ^ Target
501	//
502	// Inputs and outputs written as file paths:
503	//
504	// input location (source to target): ../../../para_2/account32_default
505	// context (root to source): para_1/account20_default/account20_default
506	// =>
507	// output (target to source): ../../para_1/account20_default/account20_default
508	#[test]
509	fn inverter_works_in_tree() {
510		parameter_types! {
511			pub UniversalLocation: InteriorLocation = [Parachain(1), account20(), account20()].into();
512		}
513
514		let input = Location::new(3, [Parachain(2), account32()]);
515		let inverted = UniversalLocation::get().invert_target(&input).unwrap();
516		assert_eq!(inverted, Location::new(2, [Parachain(1), account20(), account20()]));
517	}
518
519	// Network Topology
520	//                                     v Source
521	// Relay -> Para 1 -> SmartContract -> Account
522	//          ^ Target
523	#[test]
524	fn inverter_uses_context_as_inverted_location() {
525		parameter_types! {
526			pub UniversalLocation: InteriorLocation = [account20(), account20()].into();
527		}
528
529		let input = Location::new(2, Here);
530		let inverted = UniversalLocation::get().invert_target(&input).unwrap();
531		assert_eq!(inverted, [account20(), account20()].into());
532	}
533
534	// Network Topology
535	//                                        v Source
536	// Relay -> Para 1 -> CollectivePallet -> Plurality
537	//          ^ Target
538	#[test]
539	fn inverter_uses_only_child_on_missing_context() {
540		parameter_types! {
541			pub UniversalLocation: InteriorLocation = PalletInstance(5).into();
542		}
543
544		let input = Location::new(2, Here);
545		let inverted = UniversalLocation::get().invert_target(&input).unwrap();
546		assert_eq!(inverted, (OnlyChild, PalletInstance(5)).into());
547	}
548
549	#[test]
550	fn inverter_errors_when_location_is_too_large() {
551		parameter_types! {
552			pub UniversalLocation: InteriorLocation = Here;
553		}
554
555		let input = Location { parents: 99, interior: [Parachain(88)].into() };
556		let inverted = UniversalLocation::get().invert_target(&input);
557		assert_eq!(inverted, Err(()));
558	}
559
560	#[test]
561	fn global_consensus_converts_for_works() {
562		parameter_types! {
563			pub UniversalLocationInNetwork1: InteriorLocation = [GlobalConsensus(ByGenesis([1; 32])), Parachain(1234)].into();
564			pub UniversalLocationInNetwork2: InteriorLocation = [GlobalConsensus(ByGenesis([2; 32])), Parachain(1234)].into();
565		}
566		let network_1 = UniversalLocationInNetwork1::get().global_consensus().expect("NetworkId");
567		let network_2 = UniversalLocationInNetwork2::get().global_consensus().expect("NetworkId");
568		let network_3 = ByGenesis([3; 32]);
569		let network_4 = ByGenesis([4; 32]);
570		let network_5 = ByGenesis([5; 32]);
571
572		let test_data = vec![
573			(Location::parent(), false),
574			(Location::new(0, Here), false),
575			(Location::new(0, [GlobalConsensus(network_1)]), false),
576			(Location::new(1, [GlobalConsensus(network_1)]), false),
577			(Location::new(2, [GlobalConsensus(network_1)]), false),
578			(Location::new(0, [GlobalConsensus(network_2)]), false),
579			(Location::new(1, [GlobalConsensus(network_2)]), false),
580			(Location::new(2, [GlobalConsensus(network_2)]), true),
581			(Location::new(0, [GlobalConsensus(network_2), Parachain(1000)]), false),
582			(Location::new(1, [GlobalConsensus(network_2), Parachain(1000)]), false),
583			(Location::new(2, [GlobalConsensus(network_2), Parachain(1000)]), false),
584		];
585
586		for (location, expected_result) in test_data {
587			let result =
588				GlobalConsensusConvertsFor::<UniversalLocationInNetwork1, [u8; 32]>::convert_location(
589					&location,
590				);
591			match result {
592				Some(account) => {
593					assert_eq!(
594						true, expected_result,
595						"expected_result: {}, but conversion passed: {:?}, location: {:?}",
596						expected_result, account, location
597					);
598					match location.unpack() {
599						(_, [GlobalConsensus(network)]) =>
600							assert_eq!(
601								account,
602								GlobalConsensusConvertsFor::<UniversalLocationInNetwork1, [u8; 32]>::from_params(network),
603								"expected_result: {}, but conversion passed: {:?}, location: {:?}", expected_result, account, location
604							),
605						_ => panic!("expected_result: {}, conversion passed: {:?}, but Location does not match expected pattern, location: {:?}", expected_result, account, location)
606					}
607				},
608				None => {
609					assert_eq!(
610						false, expected_result,
611						"expected_result: {} - but conversion failed, location: {:?}",
612						expected_result, location
613					);
614				},
615			}
616		}
617
618		// all success
619		let res_1_gc_network_3 =
620			GlobalConsensusConvertsFor::<UniversalLocationInNetwork1, [u8; 32]>::convert_location(
621				&Location::new(2, [GlobalConsensus(network_3)]),
622			)
623			.unwrap();
624		let res_2_gc_network_3 =
625			GlobalConsensusConvertsFor::<UniversalLocationInNetwork2, [u8; 32]>::convert_location(
626				&Location::new(2, [GlobalConsensus(network_3)]),
627			)
628			.unwrap();
629		let res_1_gc_network_4 =
630			GlobalConsensusConvertsFor::<UniversalLocationInNetwork1, [u8; 32]>::convert_location(
631				&Location::new(2, [GlobalConsensus(network_4)]),
632			)
633			.unwrap();
634		let res_2_gc_network_4 =
635			GlobalConsensusConvertsFor::<UniversalLocationInNetwork2, [u8; 32]>::convert_location(
636				&Location::new(2, [GlobalConsensus(network_4)]),
637			)
638			.unwrap();
639		let res_1_gc_network_5 =
640			GlobalConsensusConvertsFor::<UniversalLocationInNetwork1, [u8; 32]>::convert_location(
641				&Location::new(2, [GlobalConsensus(network_5)]),
642			)
643			.unwrap();
644		let res_2_gc_network_5 =
645			GlobalConsensusConvertsFor::<UniversalLocationInNetwork2, [u8; 32]>::convert_location(
646				&Location::new(2, [GlobalConsensus(network_5)]),
647			)
648			.unwrap();
649
650		assert_ne!(res_1_gc_network_3, res_1_gc_network_4);
651		assert_ne!(res_1_gc_network_4, res_1_gc_network_5);
652		assert_ne!(res_1_gc_network_3, res_1_gc_network_5);
653
654		assert_eq!(res_1_gc_network_3, res_2_gc_network_3);
655		assert_eq!(res_1_gc_network_4, res_2_gc_network_4);
656		assert_eq!(res_1_gc_network_5, res_2_gc_network_5);
657	}
658
659	#[test]
660	fn global_consensus_parachain_converts_for_works() {
661		parameter_types! {
662			pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis([9; 32])), Parachain(1234)].into();
663		}
664
665		let test_data = vec![
666			(Location::parent(), false),
667			(Location::new(0, [Parachain(1000)]), false),
668			(Location::new(1, [Parachain(1000)]), false),
669			(
670				Location::new(
671					2,
672					[
673						GlobalConsensus(ByGenesis([0; 32])),
674						Parachain(1000),
675						AccountId32 { network: None, id: [1; 32].into() },
676					],
677				),
678				false,
679			),
680			(Location::new(2, [GlobalConsensus(ByGenesis([0; 32]))]), false),
681			(Location::new(0, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]), false),
682			(Location::new(1, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]), false),
683			(Location::new(2, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]), true),
684			(Location::new(3, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]), false),
685			(Location::new(9, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]), false),
686		];
687
688		for (location, expected_result) in test_data {
689			let result =
690				GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_location(
691					&location,
692				);
693			let result2 =
694				ExternalConsensusLocationsConverterFor::<UniversalLocation, [u8; 32]>::convert_location(
695					&location,
696				);
697			match result {
698				Some(account) => {
699					assert_eq!(
700						true, expected_result,
701						"expected_result: {}, but conversion passed: {:?}, location: {:?}",
702						expected_result, account, location
703					);
704					match location.unpack() {
705						(_, [GlobalConsensus(network), Parachain(para_id)]) =>
706							assert_eq!(
707								account,
708								GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::from_params(network, para_id),
709								"expected_result: {}, but conversion passed: {:?}, location: {:?}", expected_result, account, location
710							),
711						_ => assert_eq!(
712							true,
713							expected_result,
714							"expected_result: {}, conversion passed: {:?}, but Location does not match expected pattern, location: {:?}", expected_result, account, location
715						)
716					}
717				},
718				None => {
719					assert_eq!(
720						false, expected_result,
721						"expected_result: {} - but conversion failed, location: {:?}",
722						expected_result, location
723					);
724				},
725			}
726			if expected_result {
727				assert_eq!(result, result2);
728			}
729		}
730
731		// all success
732		let location = Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Parachain(1000)]);
733		let res_gc_a_p1000 =
734			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_location(
735				&location,
736			)
737			.unwrap();
738		assert_eq!(
739			res_gc_a_p1000,
740			ExternalConsensusLocationsConverterFor::<UniversalLocation, [u8; 32]>::convert_location(
741				&location,
742			).unwrap()
743		);
744
745		let location = Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Parachain(1001)]);
746		let res_gc_a_p1001 =
747			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_location(
748				&location,
749			)
750			.unwrap();
751		assert_eq!(
752			res_gc_a_p1001,
753			ExternalConsensusLocationsConverterFor::<UniversalLocation, [u8; 32]>::convert_location(
754				&location,
755			).unwrap()
756		);
757
758		let location = Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Parachain(1000)]);
759		let res_gc_b_p1000 =
760			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_location(
761				&location,
762			)
763			.unwrap();
764		assert_eq!(
765			res_gc_b_p1000,
766			ExternalConsensusLocationsConverterFor::<UniversalLocation, [u8; 32]>::convert_location(
767				&location,
768			).unwrap()
769		);
770
771		let location = Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Parachain(1001)]);
772		let res_gc_b_p1001 =
773			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_location(
774				&location,
775			)
776			.unwrap();
777		assert_eq!(
778			res_gc_b_p1001,
779			ExternalConsensusLocationsConverterFor::<UniversalLocation, [u8; 32]>::convert_location(
780				&location,
781			).unwrap()
782		);
783
784		assert_ne!(res_gc_a_p1000, res_gc_a_p1001);
785		assert_ne!(res_gc_a_p1000, res_gc_b_p1000);
786		assert_ne!(res_gc_a_p1000, res_gc_b_p1001);
787		assert_ne!(res_gc_b_p1000, res_gc_b_p1001);
788		assert_ne!(res_gc_b_p1000, res_gc_a_p1001);
789		assert_ne!(res_gc_b_p1001, res_gc_a_p1001);
790	}
791
792	#[test]
793	fn remote_account_convert_on_para_sending_para_32() {
794		let mul = Location {
795			parents: 1,
796			interior: [Parachain(1), AccountId32 { network: None, id: [0u8; 32] }].into(),
797		};
798		let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
799
800		assert_eq!(
801			[
802				181, 186, 132, 152, 52, 210, 226, 199, 8, 235, 213, 242, 94, 70, 250, 170, 19, 163,
803				196, 102, 245, 14, 172, 184, 2, 148, 108, 87, 230, 163, 204, 32
804			],
805			rem_1
806		);
807
808		let mul = Location {
809			parents: 1,
810			interior: [
811				Parachain(1),
812				AccountId32 { network: Some(NetworkId::Polkadot), id: [0u8; 32] },
813			]
814			.into(),
815		};
816
817		assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1);
818
819		let mul = Location {
820			parents: 1,
821			interior: [Parachain(2), AccountId32 { network: None, id: [0u8; 32] }].into(),
822		};
823		let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
824
825		assert_eq!(
826			[
827				183, 188, 66, 169, 82, 250, 45, 30, 142, 119, 184, 55, 177, 64, 53, 114, 12, 147,
828				128, 10, 60, 45, 41, 193, 87, 18, 86, 49, 127, 233, 243, 143
829			],
830			rem_2
831		);
832
833		assert_ne!(rem_1, rem_2);
834	}
835
836	#[test]
837	fn remote_account_convert_on_para_sending_para_20() {
838		let mul = Location {
839			parents: 1,
840			interior: [Parachain(1), AccountKey20 { network: None, key: [0u8; 20] }].into(),
841		};
842		let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
843
844		assert_eq!(
845			[
846				210, 60, 37, 255, 116, 38, 221, 26, 85, 82, 252, 125, 220, 19, 41, 91, 185, 69,
847				102, 83, 120, 63, 15, 212, 74, 141, 82, 203, 187, 212, 77, 120
848			],
849			rem_1
850		);
851
852		let mul = Location {
853			parents: 1,
854			interior: [
855				Parachain(1),
856				AccountKey20 { network: Some(NetworkId::Polkadot), key: [0u8; 20] },
857			]
858			.into(),
859		};
860
861		assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1);
862
863		let mul = Location {
864			parents: 1,
865			interior: [Parachain(2), AccountKey20 { network: None, key: [0u8; 20] }].into(),
866		};
867		let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
868
869		assert_eq!(
870			[
871				197, 16, 31, 199, 234, 80, 166, 55, 178, 135, 95, 48, 19, 128, 9, 167, 51, 99, 215,
872				147, 94, 171, 28, 157, 29, 107, 240, 22, 10, 104, 99, 186
873			],
874			rem_2
875		);
876
877		assert_ne!(rem_1, rem_2);
878	}
879
880	#[test]
881	fn remote_account_convert_on_para_sending_relay() {
882		let mul = Location {
883			parents: 1,
884			interior: [AccountId32 { network: None, id: [0u8; 32] }].into(),
885		};
886		let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
887
888		assert_eq!(
889			[
890				227, 12, 152, 241, 220, 53, 26, 27, 1, 167, 167, 214, 61, 161, 255, 96, 56, 16,
891				221, 59, 47, 45, 40, 193, 88, 92, 4, 167, 164, 27, 112, 99
892			],
893			rem_1
894		);
895
896		let mul = Location {
897			parents: 1,
898			interior: [AccountId32 { network: Some(NetworkId::Polkadot), id: [0u8; 32] }].into(),
899		};
900
901		assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1);
902
903		let mul = Location {
904			parents: 1,
905			interior: [AccountId32 { network: None, id: [1u8; 32] }].into(),
906		};
907		let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
908
909		assert_eq!(
910			[
911				143, 195, 87, 73, 129, 2, 163, 211, 239, 51, 55, 235, 82, 173, 162, 206, 158, 237,
912				166, 73, 254, 62, 131, 6, 170, 241, 209, 116, 105, 69, 29, 226
913			],
914			rem_2
915		);
916
917		assert_ne!(rem_1, rem_2);
918	}
919
920	#[test]
921	fn remote_account_convert_on_relay_sending_para_20() {
922		let mul = Location {
923			parents: 0,
924			interior: [Parachain(1), AccountKey20 { network: None, key: [0u8; 20] }].into(),
925		};
926		let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
927
928		assert_eq!(
929			[
930				25, 251, 15, 92, 148, 141, 236, 238, 50, 108, 133, 56, 118, 11, 250, 122, 81, 160,
931				104, 160, 97, 200, 210, 49, 208, 142, 64, 144, 24, 110, 246, 101
932			],
933			rem_1
934		);
935
936		let mul = Location {
937			parents: 0,
938			interior: [Parachain(2), AccountKey20 { network: None, key: [0u8; 20] }].into(),
939		};
940		let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
941
942		assert_eq!(
943			[
944				88, 157, 224, 235, 76, 88, 201, 143, 206, 227, 14, 192, 177, 245, 75, 62, 41, 10,
945				107, 182, 61, 57, 239, 112, 43, 151, 58, 111, 150, 153, 234, 189
946			],
947			rem_2
948		);
949
950		assert_ne!(rem_1, rem_2);
951	}
952
953	#[test]
954	fn remote_account_convert_on_relay_sending_para_32() {
955		let mul = Location {
956			parents: 0,
957			interior: [Parachain(1), AccountId32 { network: None, id: [0u8; 32] }].into(),
958		};
959		let rem_1 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
960
961		assert_eq!(
962			[
963				45, 120, 232, 0, 226, 49, 106, 48, 65, 181, 184, 147, 224, 235, 198, 152, 183, 156,
964				67, 57, 67, 67, 187, 104, 171, 23, 140, 21, 183, 152, 63, 20
965			],
966			rem_1
967		);
968
969		let mul = Location {
970			parents: 0,
971			interior: [
972				Parachain(1),
973				AccountId32 { network: Some(NetworkId::Polkadot), id: [0u8; 32] },
974			]
975			.into(),
976		};
977
978		assert_eq!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap(), rem_1);
979
980		let mul = Location {
981			parents: 0,
982			interior: [Parachain(2), AccountId32 { network: None, id: [0u8; 32] }].into(),
983		};
984		let rem_2 = ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).unwrap();
985
986		assert_eq!(
987			[
988				97, 119, 110, 66, 239, 113, 96, 234, 127, 92, 66, 204, 53, 129, 33, 119, 213, 192,
989				171, 100, 139, 51, 39, 62, 196, 163, 16, 213, 160, 44, 100, 228
990			],
991			rem_2
992		);
993
994		assert_ne!(rem_1, rem_2);
995	}
996
997	#[test]
998	fn remote_account_fails_with_bad_location() {
999		let mul = Location {
1000			parents: 1,
1001			interior: [AccountKey20 { network: None, key: [0u8; 20] }].into(),
1002		};
1003		assert!(ForeignChainAliasAccount::<[u8; 32]>::convert_location(&mul).is_none());
1004	}
1005
1006	#[test]
1007	fn remote_account_convert_on_para_sending_from_remote_para_treasury() {
1008		let relay_treasury_to_para_location =
1009			Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
1010		let actual_description = ForeignChainAliasTreasuryAccount::<[u8; 32]>::convert_location(
1011			&relay_treasury_to_para_location,
1012		)
1013		.unwrap();
1014
1015		assert_eq!(
1016			[
1017				18, 84, 93, 74, 187, 212, 254, 71, 192, 127, 112, 51, 3, 42, 54, 24, 220, 185, 161,
1018				67, 205, 154, 108, 116, 108, 166, 226, 211, 29, 11, 244, 115
1019			],
1020			actual_description
1021		);
1022
1023		let para_to_para_treasury_location = Location::new(
1024			1,
1025			[Parachain(1001), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }],
1026		);
1027		let actual_description = ForeignChainAliasTreasuryAccount::<[u8; 32]>::convert_location(
1028			&para_to_para_treasury_location,
1029		)
1030		.unwrap();
1031
1032		assert_eq!(
1033			[
1034				202, 52, 249, 30, 7, 99, 135, 128, 153, 139, 176, 141, 138, 234, 163, 150, 7, 36,
1035				204, 92, 220, 137, 87, 57, 73, 91, 243, 189, 245, 200, 217, 204
1036			],
1037			actual_description
1038		);
1039	}
1040
1041	#[test]
1042	fn local_account_convert_on_para_from_relay_treasury() {
1043		let location =
1044			Location::new(0, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
1045
1046		parameter_types! {
1047			pub TreasuryAccountId: AccountId = AccountId::new([42u8; 32]);
1048		}
1049
1050		let actual_description =
1051			LocalTreasuryVoiceConvertsVia::<TreasuryAccountId, [u8; 32]>::convert_location(
1052				&location,
1053			)
1054			.unwrap();
1055
1056		assert_eq!(
1057			[
1058				42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
1059				42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42
1060			],
1061			actual_description
1062		);
1063	}
1064}