referrerpolicy=no-referrer-when-downgrade

staging_xcm_builder/
filter_asset_location.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
17//! Various implementations of `ContainsPair<Asset, Location>` or
18//! `Contains<(Location, Vec<Asset>)>`.
19
20use alloc::vec::Vec;
21use core::marker::PhantomData;
22use frame_support::traits::{Contains, ContainsPair, Get};
23use xcm::latest::{Asset, AssetFilter, AssetId, Location, WildAsset};
24
25/// Accepts an asset iff it is a native asset.
26pub struct NativeAsset;
27impl ContainsPair<Asset, Location> for NativeAsset {
28	fn contains(asset: &Asset, origin: &Location) -> bool {
29		tracing::trace!(target: "xcm::contains", ?asset, ?origin, "NativeAsset");
30		matches!(asset.id, AssetId(ref id) if id == origin)
31	}
32}
33
34/// Accepts an asset if it is contained in the given `T`'s `Get` implementation.
35pub struct Case<T>(PhantomData<T>);
36impl<T: Get<(AssetFilter, Location)>> ContainsPair<Asset, Location> for Case<T> {
37	fn contains(asset: &Asset, origin: &Location) -> bool {
38		tracing::trace!(target: "xcm::contains", ?asset, ?origin, "Case asset");
39		let (a, o) = T::get();
40		a.matches(asset) && &o == origin
41	}
42}
43
44/// Accepts a tuple `(location, assets)` if the `location` is contained in the `Contains`
45/// implementation of the given `Location` and if every asset from `assets` matches at least one of
46/// the `AssetFilter` instances provided by the `Get` implementation of `AssetFilters`.
47pub struct LocationWithAssetFilters<LocationFilter, AssetFilters>(
48	core::marker::PhantomData<(LocationFilter, AssetFilters)>,
49);
50impl<LocationFilter: Contains<Location>, AssetFilters: Get<Vec<AssetFilter>>>
51	Contains<(Location, Vec<Asset>)> for LocationWithAssetFilters<LocationFilter, AssetFilters>
52{
53	fn contains((location, assets): &(Location, Vec<Asset>)) -> bool {
54		tracing::trace!(target: "xcm::contains", ?location, ?assets, "LocationWithAssetFilters");
55
56		// `location` must match the `Location` filter.
57		if !LocationFilter::contains(location) {
58			return false
59		}
60
61		// All `assets` must match at least one of the `AssetFilters`.
62		let filters = AssetFilters::get();
63		assets.iter().all(|asset| {
64			for filter in &filters {
65				if filter.matches(asset) {
66					return true
67				}
68			}
69			false
70		})
71	}
72}
73
74/// Implementation of `Get<Vec<AssetFilter>>` which accepts every asset.
75/// (For example, it can be used with `LocationWithAssetFilters`).
76pub struct AllAssets;
77impl Get<Vec<AssetFilter>> for AllAssets {
78	fn get() -> Vec<AssetFilter> {
79		alloc::vec![AssetFilter::Wild(WildAsset::All)]
80	}
81}
82
83#[cfg(test)]
84mod tests {
85	use super::*;
86	use frame_support::traits::Equals;
87	use xcm::latest::prelude::*;
88
89	#[test]
90	fn location_with_asset_filters_works() {
91		frame_support::parameter_types! {
92			pub ParaA: Location = Location::new(1, [Parachain(1001)]);
93			pub ParaB: Location = Location::new(1, [Parachain(1002)]);
94			pub ParaC: Location = Location::new(1, [Parachain(1003)]);
95
96			pub AssetXLocation: Location = Location::new(1, [GeneralIndex(1111)]);
97			pub AssetYLocation: Location = Location::new(1, [GeneralIndex(2222)]);
98			pub AssetZLocation: Location = Location::new(1, [GeneralIndex(3333)]);
99
100			pub OnlyAssetXOrAssetY: alloc::vec::Vec<AssetFilter> = alloc::vec![
101				Wild(AllOf { fun: WildFungible, id: AssetId(AssetXLocation::get()) }),
102				Wild(AllOf { fun: WildFungible, id: AssetId(AssetYLocation::get()) }),
103			];
104			pub OnlyAssetZ: alloc::vec::Vec<AssetFilter> = alloc::vec![
105				Wild(AllOf { fun: WildFungible, id: AssetId(AssetZLocation::get()) })
106			];
107		}
108
109		let test_data: Vec<(Location, Vec<Asset>, bool)> = vec![
110			(ParaA::get(), vec![(AssetXLocation::get(), 1).into()], true),
111			(ParaA::get(), vec![(AssetYLocation::get(), 1).into()], true),
112			(ParaA::get(), vec![(AssetZLocation::get(), 1).into()], false),
113			(
114				ParaA::get(),
115				vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
116				true,
117			),
118			(
119				ParaA::get(),
120				vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
121				false,
122			),
123			(
124				ParaA::get(),
125				vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
126				false,
127			),
128			(
129				ParaA::get(),
130				vec![
131					(AssetXLocation::get(), 1).into(),
132					(AssetYLocation::get(), 1).into(),
133					(AssetZLocation::get(), 1).into(),
134				],
135				false,
136			),
137			(ParaB::get(), vec![(AssetXLocation::get(), 1).into()], false),
138			(ParaB::get(), vec![(AssetYLocation::get(), 1).into()], false),
139			(ParaB::get(), vec![(AssetZLocation::get(), 1).into()], true),
140			(
141				ParaB::get(),
142				vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
143				false,
144			),
145			(
146				ParaB::get(),
147				vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
148				false,
149			),
150			(
151				ParaB::get(),
152				vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
153				false,
154			),
155			(
156				ParaB::get(),
157				vec![
158					(AssetXLocation::get(), 1).into(),
159					(AssetYLocation::get(), 1).into(),
160					(AssetZLocation::get(), 1).into(),
161				],
162				false,
163			),
164			(ParaC::get(), vec![(AssetXLocation::get(), 1).into()], true),
165			(ParaC::get(), vec![(AssetYLocation::get(), 1).into()], true),
166			(ParaC::get(), vec![(AssetZLocation::get(), 1).into()], true),
167			(
168				ParaC::get(),
169				vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
170				true,
171			),
172			(
173				ParaC::get(),
174				vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
175				true,
176			),
177			(
178				ParaC::get(),
179				vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
180				true,
181			),
182			(
183				ParaC::get(),
184				vec![
185					(AssetXLocation::get(), 1).into(),
186					(AssetYLocation::get(), 1).into(),
187					(AssetZLocation::get(), 1).into(),
188				],
189				true,
190			),
191		];
192
193		type Filter = (
194			// For ParaA accept only asset X and Y.
195			LocationWithAssetFilters<Equals<ParaA>, OnlyAssetXOrAssetY>,
196			// For ParaB accept only asset Z.
197			LocationWithAssetFilters<Equals<ParaB>, OnlyAssetZ>,
198			// For ParaC accept all assets.
199			LocationWithAssetFilters<Equals<ParaC>, AllAssets>,
200		);
201
202		for (location, assets, expected_result) in test_data {
203			assert_eq!(
204				Filter::contains(&(location.clone(), assets.clone())),
205				expected_result,
206				"expected_result: {expected_result} not matched for (location, assets): ({:?}, {:?})!", location, assets,
207			)
208		}
209	}
210}