referrerpolicy=no-referrer-when-downgrade

assets_common/
matching.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use core::fmt::Debug;
17use cumulus_primitives_core::ParaId;
18use frame_support::{
19	pallet_prelude::Get,
20	traits::{Contains, ContainsPair},
21};
22use xcm::prelude::*;
23
24use xcm_builder::ensure_is_remote;
25
26frame_support::parameter_types! {
27	pub LocalLocationPattern: Location = Location::new(0, Here);
28	pub ParentLocation: Location = Location::parent();
29}
30
31/// Accepts an asset if it is from the origin.
32pub struct IsForeignConcreteAsset<IsForeign>(core::marker::PhantomData<IsForeign>);
33impl<IsForeign: ContainsPair<Location, Location>> ContainsPair<Asset, Location>
34	for IsForeignConcreteAsset<IsForeign>
35{
36	fn contains(asset: &Asset, origin: &Location) -> bool {
37		let result = matches!(asset.id, AssetId(ref id) if IsForeign::contains(id, origin));
38		tracing::trace!(target: "xcm::contains", ?asset, ?origin, ?result, "IsForeignConcreteAsset");
39		result
40	}
41}
42
43/// Checks if `a` is from sibling location `b`. Checks that `Location-a` starts with
44/// `Location-b`, and that the `ParaId` of `b` is not equal to `a`.
45pub struct FromSiblingParachain<SelfParaId, L = Location>(
46	core::marker::PhantomData<(SelfParaId, L)>,
47);
48impl<SelfParaId: Get<ParaId>, L: TryFrom<Location> + TryInto<Location> + Clone + Debug>
49	ContainsPair<L, L> for FromSiblingParachain<SelfParaId, L>
50{
51	fn contains(a: &L, b: &L) -> bool {
52		tracing::trace!(target: "xcm:contains", ?a, ?b, "FromSiblingParachain");
53		// We convert locations to latest
54		let a = match ((*a).clone().try_into(), (*b).clone().try_into()) {
55			(Ok(a), Ok(b)) if a.starts_with(&b) => a, // `a` needs to be from `b` at least
56			_ => return false,
57		};
58
59		// here we check if sibling
60		match a.unpack() {
61			(1, interior) =>
62				matches!(interior.first(), Some(Parachain(sibling_para_id)) if sibling_para_id.ne(&u32::from(SelfParaId::get()))),
63			_ => false,
64		}
65	}
66}
67
68/// Checks if `a` is from the expected global consensus network. Checks that `Location-a`
69/// starts with `Location-b`, and that network is a foreign consensus system.
70pub struct FromNetwork<UniversalLocation, ExpectedNetworkId, L = Location>(
71	core::marker::PhantomData<(UniversalLocation, ExpectedNetworkId, L)>,
72);
73impl<
74		UniversalLocation: Get<InteriorLocation>,
75		ExpectedNetworkId: Get<NetworkId>,
76		L: TryFrom<Location> + TryInto<Location> + Clone + Debug,
77	> ContainsPair<L, L> for FromNetwork<UniversalLocation, ExpectedNetworkId, L>
78{
79	fn contains(a: &L, b: &L) -> bool {
80		tracing::trace!(target: "xcm:contains", ?a, ?b, "FromNetwork");
81		// We convert locations to latest
82		let a = match ((*a).clone().try_into(), (*b).clone().try_into()) {
83			(Ok(a), Ok(b)) if a.starts_with(&b) => a, // `a` needs to be from `b` at least
84			_ => return false,
85		};
86
87		let universal_source = UniversalLocation::get();
88
89		// ensure that `a` is remote and from the expected network
90		match ensure_is_remote(universal_source.clone(), a.clone()) {
91			Ok((network_id, _)) => network_id == ExpectedNetworkId::get(),
92			Err(e) => {
93				tracing::debug!(target: "xcm::contains", origin = ?a, ?universal_source, error = ?e, "FromNetwork origin is not remote to the universal_source");
94				false
95			},
96		}
97	}
98}
99
100/// Accept an asset if it is native to `AssetsAllowedNetworks` and it is coming from
101/// `OriginLocation`.
102pub struct RemoteAssetFromLocation<AssetsAllowedNetworks, OriginLocation>(
103	core::marker::PhantomData<(AssetsAllowedNetworks, OriginLocation)>,
104);
105impl<
106		L: TryInto<Location> + Clone,
107		AssetsAllowedNetworks: Contains<Location>,
108		OriginLocation: Get<Location>,
109	> ContainsPair<L, L> for RemoteAssetFromLocation<AssetsAllowedNetworks, OriginLocation>
110{
111	fn contains(asset: &L, origin: &L) -> bool {
112		let Ok(asset) = asset.clone().try_into() else {
113			return false;
114		};
115		let Ok(origin) = origin.clone().try_into() else {
116			return false;
117		};
118		let expected_origin = OriginLocation::get();
119		// ensure `origin` is expected `OriginLocation`
120		if !expected_origin.eq(&origin) {
121			tracing::trace!(
122				target: "xcm::contains",
123				?asset,
124				?origin,
125				?expected_origin,
126				"RemoteAssetFromLocation: Asset is not from expected origin"
127			);
128			return false;
129		} else {
130			tracing::trace!(
131				target: "xcm::contains",
132				?asset,
133				?origin,
134				"RemoteAssetFromLocation",
135			);
136		}
137
138		// ensure `asset` is from remote consensus listed in `AssetsAllowedNetworks`
139		AssetsAllowedNetworks::contains(&asset)
140	}
141}
142impl<AssetsAllowedNetworks: Contains<Location>, OriginLocation: Get<Location>>
143	ContainsPair<Asset, Location> for RemoteAssetFromLocation<AssetsAllowedNetworks, OriginLocation>
144{
145	fn contains(asset: &Asset, origin: &Location) -> bool {
146		tracing::trace!(target: "xcm:contains", ?asset, ?origin, "RemoteAssetFromLocation");
147		<Self as ContainsPair<Location, Location>>::contains(&asset.id.0, origin)
148	}
149}
150
151#[cfg(test)]
152mod tests {
153	use super::*;
154	use frame_support::parameter_types;
155	use xcm::latest::{ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH};
156
157	parameter_types! {
158		pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000)].into();
159		pub ExpectedNetworkId: NetworkId = ByGenesis(WESTEND_GENESIS_HASH);
160	}
161
162	#[test]
163	fn from_network_contains_works() {
164		// asset and origin from foreign consensus works
165		let asset: Location = (
166			Parent,
167			Parent,
168			GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),
169			Parachain(1000),
170			PalletInstance(1),
171			GeneralIndex(1),
172		)
173			.into();
174		let origin: Location =
175			(Parent, Parent, GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000))
176				.into();
177		assert!(FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
178
179		// asset and origin from local consensus fails
180		let asset: Location = (
181			Parent,
182			Parent,
183			GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)),
184			Parachain(1000),
185			PalletInstance(1),
186			GeneralIndex(1),
187		)
188			.into();
189		let origin: Location =
190			(Parent, Parent, GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000))
191				.into();
192		assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
193
194		// asset and origin from here fails
195		let asset: Location = (PalletInstance(1), GeneralIndex(1)).into();
196		let origin: Location = Here.into();
197		assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
198
199		// asset from different consensus fails
200		let asset: Location = (
201			Parent,
202			Parent,
203			GlobalConsensus(Polkadot),
204			Parachain(1000),
205			PalletInstance(1),
206			GeneralIndex(1),
207		)
208			.into();
209		let origin: Location =
210			(Parent, Parent, GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000))
211				.into();
212		assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
213
214		// origin from different consensus fails
215		let asset: Location = (
216			Parent,
217			Parent,
218			GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),
219			Parachain(1000),
220			PalletInstance(1),
221			GeneralIndex(1),
222		)
223			.into();
224		let origin: Location = (Parent, Parent, GlobalConsensus(Polkadot), Parachain(1000)).into();
225		assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
226
227		// asset and origin from unexpected consensus fails
228		let asset: Location = (
229			Parent,
230			Parent,
231			GlobalConsensus(Polkadot),
232			Parachain(1000),
233			PalletInstance(1),
234			GeneralIndex(1),
235		)
236			.into();
237		let origin: Location = (Parent, Parent, GlobalConsensus(Polkadot), Parachain(1000)).into();
238		assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
239	}
240}