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 for `ShouldExecute`.
18

            
19
use crate::{CreateMatcher, MatchXcm};
20
use core::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
21
use frame_support::{
22
	ensure,
23
	traits::{Contains, Get, ProcessMessageError},
24
};
25
use polkadot_parachain_primitives::primitives::IsSystem;
26
use xcm::prelude::*;
27
use xcm_executor::traits::{CheckSuspension, OnResponse, Properties, ShouldExecute};
28

            
29
/// Execution barrier that just takes `max_weight` from `properties.weight_credit`.
30
///
31
/// Useful to allow XCM execution by local chain users via extrinsics.
32
/// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset
33
/// out of the local chain to another one.
34
pub struct TakeWeightCredit;
35
impl ShouldExecute for TakeWeightCredit {
36
20139
	fn should_execute<RuntimeCall>(
37
20139
		_origin: &Location,
38
20139
		_instructions: &mut [Instruction<RuntimeCall>],
39
20139
		max_weight: Weight,
40
20139
		properties: &mut Properties,
41
20139
	) -> Result<(), ProcessMessageError> {
42
20139
		log::trace!(
43
			target: "xcm::barriers",
44
			"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
45
			_origin, _instructions, max_weight, properties,
46
		);
47
20139
		properties.weight_credit = properties
48
20139
			.weight_credit
49
20139
			.checked_sub(&max_weight)
50
20139
			.ok_or(ProcessMessageError::Overweight(max_weight))?;
51
20139
		Ok(())
52
20139
	}
53
}
54

            
55
const MAX_ASSETS_FOR_BUY_EXECUTION: usize = 2;
56

            
57
/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking
58
/// payments into account.
59
///
60
/// Only allows for `TeleportAsset`, `WithdrawAsset`, `ClaimAsset` and `ReserveAssetDeposit` XCMs
61
/// because they are the only ones that place assets in the Holding Register to pay for execution.
62
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
63
impl<T: Contains<Location>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
64
	fn should_execute<RuntimeCall>(
65
		origin: &Location,
66
		instructions: &mut [Instruction<RuntimeCall>],
67
		max_weight: Weight,
68
		_properties: &mut Properties,
69
	) -> Result<(), ProcessMessageError> {
70
		log::trace!(
71
			target: "xcm::barriers",
72
			"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
73
			origin, instructions, max_weight, _properties,
74
		);
75

            
76
		ensure!(T::contains(origin), ProcessMessageError::Unsupported);
77
		// We will read up to 5 instructions. This allows up to 3 `ClearOrigin` instructions. We
78
		// allow for more than one since anything beyond the first is a no-op and it's conceivable
79
		// that composition of operations might result in more than one being appended.
80
		let end = instructions.len().min(5);
81
		instructions[..end]
82
			.matcher()
83
			.match_next_inst(|inst| match inst {
84
				ReceiveTeleportedAsset(ref assets) |
85
				ReserveAssetDeposited(ref assets) |
86
				WithdrawAsset(ref assets) |
87
				ClaimAsset { ref assets, .. } =>
88
					if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION {
89
						Ok(())
90
					} else {
91
						Err(ProcessMessageError::BadFormat)
92
					},
93
				_ => Err(ProcessMessageError::BadFormat),
94
			})?
95
			.skip_inst_while(|inst| matches!(inst, ClearOrigin))?
96
			.match_next_inst(|inst| match inst {
97
				BuyExecution { weight_limit: Limited(ref mut weight), .. }
98
					if weight.all_gte(max_weight) =>
99
				{
100
					*weight = max_weight;
101
					Ok(())
102
				},
103
				BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
104
					*weight_limit = Limited(max_weight);
105
					Ok(())
106
				},
107
				_ => Err(ProcessMessageError::Overweight(max_weight)),
108
			})?;
109
		Ok(())
110
	}
111
}
112

            
113
/// A derivative barrier, which scans the first `MaxPrefixes` instructions for origin-alterers and
114
/// then evaluates `should_execute` of the `InnerBarrier` based on the remaining instructions and
115
/// the newly computed origin.
116
///
117
/// This effectively allows for the possibility of distinguishing an origin which is acting as a
118
/// router for its derivative locations (or as a bridge for a remote location) and an origin which
119
/// is actually trying to send a message for itself. In the former case, the message will be
120
/// prefixed with origin-mutating instructions.
121
///
122
/// Any barriers which should be interpreted based on the computed origin rather than the original
123
/// message origin should be subject to this. This is the case for most barriers since the
124
/// effective origin is generally more important than the routing origin. Any other barriers, and
125
/// especially those which should be interpreted only the routing origin should not be subject to
126
/// this.
127
///
128
/// E.g.
129
/// ```nocompile
130
/// type MyBarrier = (
131
/// 	TakeWeightCredit,
132
/// 	AllowTopLevelPaidExecutionFrom<DirectCustomerLocations>,
133
/// 	WithComputedOrigin<(
134
/// 		AllowTopLevelPaidExecutionFrom<DerivativeCustomerLocations>,
135
/// 		AllowUnpaidExecutionFrom<ParentLocation>,
136
/// 		AllowSubscriptionsFrom<AllowedSubscribers>,
137
/// 		AllowKnownQueryResponses<TheResponseHandler>,
138
/// 	)>,
139
/// );
140
/// ```
141
///
142
/// In the above example, `AllowUnpaidExecutionFrom` appears once underneath
143
/// `WithComputedOrigin`. This is in order to distinguish between messages which are notionally
144
/// from a derivative location of `ParentLocation` but that just happened to be sent via
145
/// `ParentLocation` rather than messages that were sent by the parent.
146
///
147
/// Similarly `AllowTopLevelPaidExecutionFrom` appears twice: once inside of `WithComputedOrigin`
148
/// where we provide the list of origins which are derivative origins, and then secondly outside
149
/// of `WithComputedOrigin` where we provide the list of locations which are direct origins. It's
150
/// reasonable for these lists to be merged into one and that used both inside and out.
151
///
152
/// Finally, we see `AllowSubscriptionsFrom` and `AllowKnownQueryResponses` are both inside of
153
/// `WithComputedOrigin`. This means that if a message begins with origin-mutating instructions,
154
/// then it must be the finally computed origin which we accept subscriptions or expect a query
155
/// response from. For example, even if an origin appeared in the `AllowedSubscribers` list, we
156
/// would ignore this rule if it began with origin mutators and they changed the origin to something
157
/// which was not on the list.
158
pub struct WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>(
159
	PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>,
160
);
161
impl<InnerBarrier: ShouldExecute, LocalUniversal: Get<InteriorLocation>, MaxPrefixes: Get<u32>>
162
	ShouldExecute for WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>
163
{
164
	fn should_execute<Call>(
165
		origin: &Location,
166
		instructions: &mut [Instruction<Call>],
167
		max_weight: Weight,
168
		properties: &mut Properties,
169
	) -> Result<(), ProcessMessageError> {
170
		log::trace!(
171
			target: "xcm::barriers",
172
			"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
173
			origin, instructions, max_weight, properties,
174
		);
175
		let mut actual_origin = origin.clone();
176
		let skipped = Cell::new(0usize);
177
		// NOTE: We do not check the validity of `UniversalOrigin` here, meaning that a malicious
178
		// origin could place a `UniversalOrigin` in order to spoof some location which gets free
179
		// execution. This technical could get it past the barrier condition, but the execution
180
		// would instantly fail since the first instruction would cause an error with the
181
		// invalid UniversalOrigin.
182
		instructions.matcher().match_next_inst_while(
183
			|_| skipped.get() < MaxPrefixes::get() as usize,
184
			|inst| {
185
				match inst {
186
					UniversalOrigin(new_global) => {
187
						// Note the origin is *relative to local consensus*! So we need to escape
188
						// local consensus with the `parents` before diving in into the
189
						// `universal_location`.
190
						actual_origin =
191
							Junctions::from([*new_global]).relative_to(&LocalUniversal::get());
192
					},
193
					DescendOrigin(j) => {
194
						let Ok(_) = actual_origin.append_with(j.clone()) else {
195
							return Err(ProcessMessageError::Unsupported)
196
						};
197
					},
198
					_ => return Ok(ControlFlow::Break(())),
199
				};
200
				skipped.set(skipped.get() + 1);
201
				Ok(ControlFlow::Continue(()))
202
			},
203
		)?;
204
		InnerBarrier::should_execute(
205
			&actual_origin,
206
			&mut instructions[skipped.get()..],
207
			max_weight,
208
			properties,
209
		)
210
	}
211
}
212

            
213
/// Sets the message ID to `t` using a `SetTopic(t)` in the last position if present.
214
///
215
/// Note that the message ID does not necessarily have to be unique; it is the
216
/// sender's responsibility to ensure uniqueness.
217
///
218
/// Requires some inner barrier to pass on the rest of the message.
219
pub struct TrailingSetTopicAsId<InnerBarrier>(PhantomData<InnerBarrier>);
220
impl<InnerBarrier: ShouldExecute> ShouldExecute for TrailingSetTopicAsId<InnerBarrier> {
221
20139
	fn should_execute<Call>(
222
20139
		origin: &Location,
223
20139
		instructions: &mut [Instruction<Call>],
224
20139
		max_weight: Weight,
225
20139
		properties: &mut Properties,
226
20139
	) -> Result<(), ProcessMessageError> {
227
20139
		log::trace!(
228
			target: "xcm::barriers",
229
			"TrailingSetTopicAsId origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
230
			origin, instructions, max_weight, properties,
231
		);
232
20139
		let until = if let Some(SetTopic(t)) = instructions.last() {
233
36
			properties.message_id = Some(*t);
234
36
			instructions.len() - 1
235
		} else {
236
20103
			instructions.len()
237
		};
238
20139
		InnerBarrier::should_execute(&origin, &mut instructions[..until], max_weight, properties)
239
20139
	}
240
}
241

            
242
/// Barrier condition that allows for a `SuspensionChecker` that controls whether or not the XCM
243
/// executor will be suspended from executing the given XCM.
244
pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>);
245
impl<Inner, SuspensionChecker> ShouldExecute for RespectSuspension<Inner, SuspensionChecker>
246
where
247
	Inner: ShouldExecute,
248
	SuspensionChecker: CheckSuspension,
249
{
250
	fn should_execute<Call>(
251
		origin: &Location,
252
		instructions: &mut [Instruction<Call>],
253
		max_weight: Weight,
254
		properties: &mut Properties,
255
	) -> Result<(), ProcessMessageError> {
256
		if SuspensionChecker::is_suspended(origin, instructions, max_weight, properties) {
257
			Err(ProcessMessageError::Yield)
258
		} else {
259
			Inner::should_execute(origin, instructions, max_weight, properties)
260
		}
261
	}
262
}
263

            
264
/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`).
265
///
266
/// Use only for executions from completely trusted origins, from which no permissionless messages
267
/// can be sent.
268
pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
269
impl<T: Contains<Location>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
270
	fn should_execute<RuntimeCall>(
271
		origin: &Location,
272
		instructions: &mut [Instruction<RuntimeCall>],
273
		_max_weight: Weight,
274
		_properties: &mut Properties,
275
	) -> Result<(), ProcessMessageError> {
276
		log::trace!(
277
			target: "xcm::barriers",
278
			"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
279
			origin, instructions, _max_weight, _properties,
280
		);
281
		ensure!(T::contains(origin), ProcessMessageError::Unsupported);
282
		Ok(())
283
	}
284
}
285

            
286
/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`) if the
287
/// message begins with the instruction `UnpaidExecution`.
288
///
289
/// Use only for executions from trusted origin groups.
290
pub struct AllowExplicitUnpaidExecutionFrom<T>(PhantomData<T>);
291
impl<T: Contains<Location>> ShouldExecute for AllowExplicitUnpaidExecutionFrom<T> {
292
	fn should_execute<Call>(
293
		origin: &Location,
294
		instructions: &mut [Instruction<Call>],
295
		max_weight: Weight,
296
		_properties: &mut Properties,
297
	) -> Result<(), ProcessMessageError> {
298
		log::trace!(
299
			target: "xcm::barriers",
300
			"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
301
			origin, instructions, max_weight, _properties,
302
		);
303
		ensure!(T::contains(origin), ProcessMessageError::Unsupported);
304
		instructions.matcher().match_next_inst(|inst| match inst {
305
			UnpaidExecution { weight_limit: Limited(m), .. } if m.all_gte(max_weight) => Ok(()),
306
			UnpaidExecution { weight_limit: Unlimited, .. } => Ok(()),
307
			_ => Err(ProcessMessageError::Overweight(max_weight)),
308
		})?;
309
		Ok(())
310
	}
311
}
312

            
313
/// Allows a message only if it is from a system-level child parachain.
314
pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>);
315
impl<ParaId: IsSystem + From<u32>> Contains<Location> for IsChildSystemParachain<ParaId> {
316
1584
	fn contains(l: &Location) -> bool {
317
		matches!(
318
1584
			l.interior().as_slice(),
319
			[Junction::Parachain(id)]
320
				if ParaId::from(*id).is_system() && l.parent_count() == 0,
321
		)
322
1584
	}
323
}
324

            
325
/// Matches if the given location is a system-level sibling parachain.
326
pub struct IsSiblingSystemParachain<ParaId, SelfParaId>(PhantomData<(ParaId, SelfParaId)>);
327
impl<ParaId: IsSystem + From<u32> + Eq, SelfParaId: Get<ParaId>> Contains<Location>
328
	for IsSiblingSystemParachain<ParaId, SelfParaId>
329
{
330
	fn contains(l: &Location) -> bool {
331
		matches!(
332
			l.unpack(),
333
			(1, [Junction::Parachain(id)])
334
				if SelfParaId::get() != ParaId::from(*id) && ParaId::from(*id).is_system(),
335
		)
336
	}
337
}
338

            
339
/// Matches if the given location contains only the specified amount of parents and no interior
340
/// junctions.
341
pub struct IsParentsOnly<Count>(PhantomData<Count>);
342
impl<Count: Get<u8>> Contains<Location> for IsParentsOnly<Count> {
343
	fn contains(t: &Location) -> bool {
344
		t.contains_parents_only(Count::get())
345
	}
346
}
347

            
348
/// Allows only messages if the generic `ResponseHandler` expects them via `expecting_response`.
349
pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
350
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
351
	fn should_execute<RuntimeCall>(
352
		origin: &Location,
353
		instructions: &mut [Instruction<RuntimeCall>],
354
		_max_weight: Weight,
355
		_properties: &mut Properties,
356
	) -> Result<(), ProcessMessageError> {
357
		log::trace!(
358
			target: "xcm::barriers",
359
			"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
360
			origin, instructions, _max_weight, _properties,
361
		);
362
		instructions
363
			.matcher()
364
			.assert_remaining_insts(1)?
365
			.match_next_inst(|inst| match inst {
366
				QueryResponse { query_id, querier, .. }
367
					if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
368
					Ok(()),
369
				_ => Err(ProcessMessageError::BadFormat),
370
			})?;
371
		Ok(())
372
	}
373
}
374

            
375
/// Allows execution from `origin` if it is just a straight `SubscribeVersion` or
376
/// `UnsubscribeVersion` instruction.
377
pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
378
impl<T: Contains<Location>> ShouldExecute for AllowSubscriptionsFrom<T> {
379
	fn should_execute<RuntimeCall>(
380
		origin: &Location,
381
		instructions: &mut [Instruction<RuntimeCall>],
382
		_max_weight: Weight,
383
		_properties: &mut Properties,
384
	) -> Result<(), ProcessMessageError> {
385
		log::trace!(
386
			target: "xcm::barriers",
387
			"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
388
			origin, instructions, _max_weight, _properties,
389
		);
390
		ensure!(T::contains(origin), ProcessMessageError::Unsupported);
391
		instructions
392
			.matcher()
393
			.assert_remaining_insts(1)?
394
			.match_next_inst(|inst| match inst {
395
				SubscribeVersion { .. } | UnsubscribeVersion => Ok(()),
396
				_ => Err(ProcessMessageError::BadFormat),
397
			})?;
398
		Ok(())
399
	}
400
}
401

            
402
/// Allows execution for the Relay Chain origin (represented as `Location::parent()`) if it is just
403
/// a straight `HrmpNewChannelOpenRequest`, `HrmpChannelAccepted`, or `HrmpChannelClosing`
404
/// instruction.
405
///
406
/// Note: This barrier fulfills safety recommendations for the mentioned instructions - see their
407
/// documentation.
408
pub struct AllowHrmpNotificationsFromRelayChain;
409
impl ShouldExecute for AllowHrmpNotificationsFromRelayChain {
410
	fn should_execute<RuntimeCall>(
411
		origin: &Location,
412
		instructions: &mut [Instruction<RuntimeCall>],
413
		_max_weight: Weight,
414
		_properties: &mut Properties,
415
	) -> Result<(), ProcessMessageError> {
416
		log::trace!(
417
			target: "xcm::barriers",
418
			"AllowHrmpNotificationsFromRelayChain origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
419
			origin, instructions, _max_weight, _properties,
420
		);
421
		// accept only the Relay Chain
422
		ensure!(matches!(origin.unpack(), (1, [])), ProcessMessageError::Unsupported);
423
		// accept only HRMP notifications and nothing else
424
		instructions
425
			.matcher()
426
			.assert_remaining_insts(1)?
427
			.match_next_inst(|inst| match inst {
428
				HrmpNewChannelOpenRequest { .. } |
429
				HrmpChannelAccepted { .. } |
430
				HrmpChannelClosing { .. } => Ok(()),
431
				_ => Err(ProcessMessageError::BadFormat),
432
			})?;
433
		Ok(())
434
	}
435
}
436

            
437
/// Deny executing the XCM if it matches any of the Deny filter regardless of anything else.
438
/// If it passes the Deny, and matches one of the Allow cases then it is let through.
439
pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
440
where
441
	Deny: ShouldExecute,
442
	Allow: ShouldExecute;
443

            
444
impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
445
where
446
	Deny: ShouldExecute,
447
	Allow: ShouldExecute,
448
{
449
	fn should_execute<RuntimeCall>(
450
		origin: &Location,
451
		message: &mut [Instruction<RuntimeCall>],
452
		max_weight: Weight,
453
		properties: &mut Properties,
454
	) -> Result<(), ProcessMessageError> {
455
		Deny::should_execute(origin, message, max_weight, properties)?;
456
		Allow::should_execute(origin, message, max_weight, properties)
457
	}
458
}
459

            
460
// See issue <https://github.com/paritytech/polkadot/issues/5233>
461
pub struct DenyReserveTransferToRelayChain;
462
impl ShouldExecute for DenyReserveTransferToRelayChain {
463
	fn should_execute<RuntimeCall>(
464
		origin: &Location,
465
		message: &mut [Instruction<RuntimeCall>],
466
		_max_weight: Weight,
467
		_properties: &mut Properties,
468
	) -> Result<(), ProcessMessageError> {
469
		message.matcher().match_next_inst_while(
470
			|_| true,
471
			|inst| match inst {
472
				InitiateReserveWithdraw {
473
					reserve: Location { parents: 1, interior: Here },
474
					..
475
				} |
476
				DepositReserveAsset { dest: Location { parents: 1, interior: Here }, .. } |
477
				TransferReserveAsset { dest: Location { parents: 1, interior: Here }, .. } => {
478
					Err(ProcessMessageError::Unsupported) // Deny
479
				},
480

            
481
				// An unexpected reserve transfer has arrived from the Relay Chain. Generally,
482
				// `IsReserve` should not allow this, but we just log it here.
483
				ReserveAssetDeposited { .. }
484
					if matches!(origin, Location { parents: 1, interior: Here }) =>
485
				{
486
					log::warn!(
487
						target: "xcm::barrier",
488
						"Unexpected ReserveAssetDeposited from the Relay Chain",
489
					);
490
					Ok(ControlFlow::Continue(()))
491
				},
492

            
493
				_ => Ok(ControlFlow::Continue(())),
494
			},
495
		)?;
496

            
497
		// Permit everything else
498
		Ok(())
499
	}
500
}