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