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