pezstaging_xcm_builder/
location_conversion.rs

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