referrerpolicy=no-referrer-when-downgrade

staging_xcm/v5/
traits.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//! Cross-Consensus Message format data structures.
18
19pub use crate::v3::{Error as OldError, SendError, XcmHash};
20use codec::{Decode, DecodeWithMemTracking, Encode};
21use core::result;
22use scale_info::TypeInfo;
23
24pub use sp_weights::Weight;
25
26use super::*;
27
28/// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM
29/// format. Those trailing are merely part of the XCM implementation; there is no expectation that
30/// they will retain the same index over time.
31#[derive(
32	Copy,
33	Clone,
34	Encode,
35	Decode,
36	DecodeWithMemTracking,
37	Eq,
38	PartialEq,
39	Debug,
40	TypeInfo,
41	MaxEncodedLen,
42)]
43#[scale_info(replace_segment("staging_xcm", "xcm"))]
44#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
45pub enum Error {
46	// Errors that happen due to instructions being executed. These alone are defined in the
47	// XCM specification.
48	/// An arithmetic overflow happened.
49	#[codec(index = 0)]
50	Overflow,
51	/// The instruction is intentionally unsupported.
52	#[codec(index = 1)]
53	Unimplemented,
54	/// Origin Register does not contain a value value for a reserve transfer notification.
55	#[codec(index = 2)]
56	UntrustedReserveLocation,
57	/// Origin Register does not contain a value value for a teleport notification.
58	#[codec(index = 3)]
59	UntrustedTeleportLocation,
60	/// `MultiLocation` value too large to descend further.
61	#[codec(index = 4)]
62	LocationFull,
63	/// `MultiLocation` value ascend more parents than known ancestors of local location.
64	#[codec(index = 5)]
65	LocationNotInvertible,
66	/// The Origin Register does not contain a valid value for instruction.
67	#[codec(index = 6)]
68	BadOrigin,
69	/// The location parameter is not a valid value for the instruction.
70	#[codec(index = 7)]
71	InvalidLocation,
72	/// The given asset is not handled.
73	#[codec(index = 8)]
74	AssetNotFound,
75	/// An asset transaction (like withdraw or deposit) failed (typically due to type conversions).
76	#[codec(index = 9)]
77	FailedToTransactAsset(#[codec(skip)] &'static str),
78	/// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights.
79	#[codec(index = 10)]
80	NotWithdrawable,
81	/// An asset cannot be deposited under the ownership of a particular location.
82	#[codec(index = 11)]
83	LocationCannotHold,
84	/// Attempt to send a message greater than the maximum supported by the transport protocol.
85	#[codec(index = 12)]
86	ExceedsMaxMessageSize,
87	/// The given message cannot be translated into a format supported by the destination.
88	#[codec(index = 13)]
89	DestinationUnsupported,
90	/// Destination is routable, but there is some issue with the transport mechanism.
91	#[codec(index = 14)]
92	Transport(#[codec(skip)] &'static str),
93	/// Destination is known to be unroutable.
94	#[codec(index = 15)]
95	Unroutable,
96	/// Used by `ClaimAsset` when the given claim could not be recognized/found.
97	#[codec(index = 16)]
98	UnknownClaim,
99	/// Used by `Transact` when the functor cannot be decoded.
100	#[codec(index = 17)]
101	FailedToDecode,
102	/// Used by `Transact` to indicate that the given weight limit could be breached by the
103	/// functor.
104	#[codec(index = 18)]
105	MaxWeightInvalid,
106	/// Used by `BuyExecution` when the Holding Register does not contain payable fees.
107	#[codec(index = 19)]
108	NotHoldingFees,
109	/// Used by `BuyExecution` when the fees declared to purchase weight are insufficient.
110	#[codec(index = 20)]
111	TooExpensive,
112	/// Used by the `Trap` instruction to force an error intentionally. Its code is included.
113	#[codec(index = 21)]
114	Trap(u64),
115	/// Used by `ExpectAsset`, `ExpectError` and `ExpectOrigin` when the expectation was not true.
116	#[codec(index = 22)]
117	ExpectationFalse,
118	/// The provided pallet index was not found.
119	#[codec(index = 23)]
120	PalletNotFound,
121	/// The given pallet's name is different to that expected.
122	#[codec(index = 24)]
123	NameMismatch,
124	/// The given pallet's version has an incompatible version to that expected.
125	#[codec(index = 25)]
126	VersionIncompatible,
127	/// The given operation would lead to an overflow of the Holding Register.
128	#[codec(index = 26)]
129	HoldingWouldOverflow,
130	/// The message was unable to be exported.
131	#[codec(index = 27)]
132	ExportError,
133	/// `MultiLocation` value failed to be reanchored.
134	#[codec(index = 28)]
135	ReanchorFailed,
136	/// No deal is possible under the given constraints.
137	#[codec(index = 29)]
138	NoDeal,
139	/// Fees were required which the origin could not pay.
140	#[codec(index = 30)]
141	FeesNotMet,
142	/// Some other error with locking.
143	#[codec(index = 31)]
144	LockError,
145	/// The state was not in a condition where the operation was valid to make.
146	#[codec(index = 32)]
147	NoPermission,
148	/// The universal location of the local consensus is improper.
149	#[codec(index = 33)]
150	Unanchored,
151	/// An asset cannot be deposited, probably because (too much of) it already exists.
152	#[codec(index = 34)]
153	NotDepositable,
154	/// Too many assets matched the given asset filter.
155	#[codec(index = 35)]
156	TooManyAssets,
157
158	// Errors that happen prior to instructions being executed. These fall outside of the XCM
159	// spec.
160	/// XCM version not able to be handled.
161	UnhandledXcmVersion,
162	/// Execution of the XCM would potentially result in a greater weight used than weight limit.
163	WeightLimitReached(Weight),
164	/// The XCM did not pass the barrier condition for execution.
165	///
166	/// The barrier condition differs on different chains and in different circumstances, but
167	/// generally it means that the conditions surrounding the message were not such that the chain
168	/// considers the message worth spending time executing. Since most chains lift the barrier to
169	/// execution on appropriate payment, presentation of an NFT voucher, or based on the message
170	/// origin, it means that none of those were the case.
171	Barrier,
172	/// The weight of an XCM message is not computable ahead of execution.
173	WeightNotComputable,
174	/// Recursion stack limit reached
175	// TODO(https://github.com/paritytech/polkadot-sdk/issues/6199): This should have a fixed index since
176	// we use it in `FrameTransactionalProcessor` // which is used in instructions.
177	// Or we should create a different error for that.
178	ExceedsStackLimit,
179}
180
181impl TryFrom<OldError> for Error {
182	type Error = ();
183	fn try_from(old_error: OldError) -> result::Result<Error, ()> {
184		use OldError::*;
185		Ok(match old_error {
186			Overflow => Self::Overflow,
187			Unimplemented => Self::Unimplemented,
188			UntrustedReserveLocation => Self::UntrustedReserveLocation,
189			UntrustedTeleportLocation => Self::UntrustedTeleportLocation,
190			LocationFull => Self::LocationFull,
191			LocationNotInvertible => Self::LocationNotInvertible,
192			BadOrigin => Self::BadOrigin,
193			InvalidLocation => Self::InvalidLocation,
194			AssetNotFound => Self::AssetNotFound,
195			FailedToTransactAsset(s) => Self::FailedToTransactAsset(s),
196			NotWithdrawable => Self::NotWithdrawable,
197			LocationCannotHold => Self::LocationCannotHold,
198			ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize,
199			DestinationUnsupported => Self::DestinationUnsupported,
200			Transport(s) => Self::Transport(s),
201			Unroutable => Self::Unroutable,
202			UnknownClaim => Self::UnknownClaim,
203			FailedToDecode => Self::FailedToDecode,
204			MaxWeightInvalid => Self::MaxWeightInvalid,
205			NotHoldingFees => Self::NotHoldingFees,
206			TooExpensive => Self::TooExpensive,
207			Trap(i) => Self::Trap(i),
208			ExpectationFalse => Self::ExpectationFalse,
209			PalletNotFound => Self::PalletNotFound,
210			NameMismatch => Self::NameMismatch,
211			VersionIncompatible => Self::VersionIncompatible,
212			HoldingWouldOverflow => Self::HoldingWouldOverflow,
213			ExportError => Self::ExportError,
214			ReanchorFailed => Self::ReanchorFailed,
215			NoDeal => Self::NoDeal,
216			FeesNotMet => Self::FeesNotMet,
217			LockError => Self::LockError,
218			NoPermission => Self::NoPermission,
219			Unanchored => Self::Unanchored,
220			NotDepositable => Self::NotDepositable,
221			UnhandledXcmVersion => Self::UnhandledXcmVersion,
222			WeightLimitReached(weight) => Self::WeightLimitReached(weight),
223			Barrier => Self::Barrier,
224			WeightNotComputable => Self::WeightNotComputable,
225			ExceedsStackLimit => Self::ExceedsStackLimit,
226		})
227	}
228}
229
230impl From<SendError> for Error {
231	fn from(e: SendError) -> Self {
232		match e {
233			SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument =>
234				Error::Unroutable,
235			SendError::Transport(s) => Error::Transport(s),
236			SendError::DestinationUnsupported => Error::DestinationUnsupported,
237			SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
238			SendError::Fees => Error::FeesNotMet,
239		}
240	}
241}
242
243pub type Result = result::Result<(), Error>;
244
245/// Outcome of an XCM execution.
246#[derive(Clone, Encode, Decode, DecodeWithMemTracking, Eq, PartialEq, Debug, TypeInfo)]
247pub enum Outcome {
248	/// Execution completed successfully; given weight was used.
249	Complete { used: Weight },
250	/// Execution started, but did not complete successfully due to`error` which occurred
251	/// on the `index`-th (top-level) instruction. Overall, total `weight` was used.
252	Incomplete { used: Weight, error: InstructionError },
253	/// Execution did not start due to an error. We use `InstructionError` since it's always
254	/// possible to isolate the problematic instruction that caused the error.
255	Error(InstructionError),
256}
257
258/// XCM error and the index of the instruction that caused it.
259#[derive(Copy, Clone, Encode, Decode, DecodeWithMemTracking, Eq, PartialEq, Debug, TypeInfo)]
260pub struct InstructionError {
261	/// The index of the intruction that caused the error.
262	pub index: InstructionIndex,
263	/// The XCM error itself.
264	pub error: Error,
265}
266
267impl Outcome {
268	pub fn ensure_complete(self) -> result::Result<(), InstructionError> {
269		match self {
270			Outcome::Complete { .. } => Ok(()),
271			Outcome::Incomplete { error, .. } => Err(error),
272			Outcome::Error(error) => Err(error),
273		}
274	}
275	pub fn ensure_execution(self) -> result::Result<Weight, InstructionError> {
276		match self {
277			Outcome::Complete { used, .. } => Ok(used),
278			Outcome::Incomplete { used, .. } => Ok(used),
279			Outcome::Error(error) => Err(error),
280		}
281	}
282	/// How much weight was used by the XCM execution attempt.
283	pub fn weight_used(&self) -> Weight {
284		match self {
285			Outcome::Complete { used, .. } => *used,
286			Outcome::Incomplete { used, .. } => *used,
287			Outcome::Error(_) => Weight::zero(),
288		}
289	}
290}
291
292impl From<Error> for Outcome {
293	fn from(error: Error) -> Self {
294		Self::Error(InstructionError { error, index: 0 })
295	}
296}
297
298pub trait PreparedMessage {
299	fn weight_of(&self) -> Weight;
300}
301
302/// The index of an instruction in an XCM.
303pub type InstructionIndex = u8;
304
305/// Type of XCM message executor.
306pub trait ExecuteXcm<Call> {
307	type Prepared: PreparedMessage;
308	/// If it fails, returns the index of the problematic instruction.
309	fn prepare(
310		message: Xcm<Call>,
311		weight_limit: Weight,
312	) -> result::Result<Self::Prepared, InstructionError>;
313	fn execute(
314		origin: impl Into<Location>,
315		pre: Self::Prepared,
316		id: &mut XcmHash,
317		weight_credit: Weight,
318	) -> Outcome;
319	fn prepare_and_execute(
320		origin: impl Into<Location>,
321		message: Xcm<Call>,
322		id: &mut XcmHash,
323		weight_limit: Weight,
324		weight_credit: Weight,
325	) -> Outcome {
326		let pre = match Self::prepare(message, weight_limit) {
327			Ok(x) => x,
328			Err(error) => return Outcome::Error(error),
329		};
330		Self::execute(origin, pre, id, weight_credit)
331	}
332
333	/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
334	/// the convention for fees.
335	fn charge_fees(location: impl Into<Location>, fees: Assets) -> Result;
336}
337
338pub enum Weightless {}
339impl PreparedMessage for Weightless {
340	fn weight_of(&self) -> Weight {
341		unreachable!()
342	}
343}
344
345impl<C> ExecuteXcm<C> for () {
346	type Prepared = Weightless;
347	fn prepare(_: Xcm<C>, _: Weight) -> result::Result<Self::Prepared, InstructionError> {
348		Err(InstructionError { index: 0, error: Error::Unimplemented })
349	}
350	fn execute(_: impl Into<Location>, _: Self::Prepared, _: &mut XcmHash, _: Weight) -> Outcome {
351		unreachable!()
352	}
353	fn charge_fees(_location: impl Into<Location>, _fees: Assets) -> Result {
354		Err(Error::Unimplemented)
355	}
356}
357
358pub trait Reanchorable: Sized {
359	/// Type to return in case of an error.
360	type Error: Debug;
361
362	/// Mutate `self` so that it represents the same location from the point of view of `target`.
363	/// The context of `self` is provided as `context`.
364	///
365	/// Does not modify `self` in case of overflow.
366	fn reanchor(
367		&mut self,
368		target: &Location,
369		context: &InteriorLocation,
370	) -> core::result::Result<(), ()>;
371
372	/// Consume `self` and return a new value representing the same location from the point of view
373	/// of `target`. The context of `self` is provided as `context`.
374	///
375	/// Returns the original `self` in case of overflow.
376	fn reanchored(
377		self,
378		target: &Location,
379		context: &InteriorLocation,
380	) -> core::result::Result<Self, Self::Error>;
381}
382
383/// Result value when attempting to send an XCM message.
384pub type SendResult<T> = result::Result<(T, Assets), SendError>;
385
386/// Utility for sending an XCM message to a given location.
387///
388/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each
389/// router might return `NotApplicable` to pass the execution to the next sender item. Note that
390/// each `NotApplicable` might alter the destination and the XCM message for to the next router.
391///
392/// # Example
393/// ```rust
394/// # use codec::Encode;
395/// # use staging_xcm::v5::{prelude::*, Weight};
396/// # use staging_xcm::VersionedXcm;
397/// # use std::convert::Infallible;
398///
399/// /// A sender that only passes the message through and does nothing.
400/// struct Sender1;
401/// impl SendXcm for Sender1 {
402///     type Ticket = Infallible;
403///     fn validate(_: &mut Option<Location>, _: &mut Option<Xcm<()>>) -> SendResult<Infallible> {
404///         Err(SendError::NotApplicable)
405///     }
406///     fn deliver(_: Infallible) -> Result<XcmHash, SendError> {
407///         unreachable!()
408///     }
409/// }
410///
411/// /// A sender that accepts a message that has two junctions, otherwise stops the routing.
412/// struct Sender2;
413/// impl SendXcm for Sender2 {
414///     type Ticket = ();
415///     fn validate(destination: &mut Option<Location>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
416///         match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() {
417///             (0, [j1, j2]) => Ok(((), Assets::new())),
418///             _ => Err(SendError::Unroutable),
419///         }
420///     }
421///     fn deliver(_: ()) -> Result<XcmHash, SendError> {
422///         Ok([0; 32])
423///     }
424/// }
425///
426/// /// A sender that accepts a message from a parent, passing through otherwise.
427/// struct Sender3;
428/// impl SendXcm for Sender3 {
429///     type Ticket = ();
430///     fn validate(destination: &mut Option<Location>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
431///         match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() {
432///             (1, []) => Ok(((), Assets::new())),
433///             _ => Err(SendError::NotApplicable),
434///         }
435///     }
436///     fn deliver(_: ()) -> Result<XcmHash, SendError> {
437///         Ok([0; 32])
438///     }
439/// }
440///
441/// // A call to send via XCM. We don't really care about this.
442/// # fn main() {
443/// let call: Vec<u8> = ().encode();
444/// let message = Xcm(vec![Instruction::Transact {
445///     origin_kind: OriginKind::Superuser,
446///     call: call.into(),
447///     fallback_max_weight: None,
448/// }]);
449/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256);
450///
451/// // Sender2 will block this.
452/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err());
453///
454/// // Sender3 will catch this.
455/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok());
456/// # }
457/// ```
458pub trait SendXcm {
459	/// Intermediate value which connects the two phases of the send operation.
460	type Ticket;
461
462	/// Check whether the given `_message` is deliverable to the given `_destination` and if so
463	/// determine the cost which will be paid by this chain to do so, returning a `Validated` token
464	/// which can be used to enact delivery.
465	///
466	/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
467	/// may only be consumed if the `Err` is not `NotApplicable`.
468	///
469	/// If it is not a destination which can be reached with this type but possibly could by others,
470	/// then this *MUST* return `NotApplicable`. Any other error will cause the tuple
471	/// implementation to exit early without trying other type fields.
472	fn validate(
473		destination: &mut Option<Location>,
474		message: &mut Option<Xcm<()>>,
475	) -> SendResult<Self::Ticket>;
476
477	/// Actually carry out the delivery operation for a previously validated message sending.
478	fn deliver(ticket: Self::Ticket) -> result::Result<XcmHash, SendError>;
479
480	/// Ensure `[Self::delivery]` is successful for the given `location` when called in benchmarks.
481	#[cfg(feature = "runtime-benchmarks")]
482	fn ensure_successful_delivery(_location: Option<Location>) {}
483}
484
485#[impl_trait_for_tuples::impl_for_tuples(30)]
486impl SendXcm for Tuple {
487	for_tuples! { type Ticket = (#( Option<Tuple::Ticket> ),* ); }
488
489	fn validate(
490		destination: &mut Option<Location>,
491		message: &mut Option<Xcm<()>>,
492	) -> SendResult<Self::Ticket> {
493		let mut maybe_cost: Option<Assets> = None;
494		let one_ticket: Self::Ticket = (for_tuples! { #(
495			if maybe_cost.is_some() {
496				None
497			} else {
498				match Tuple::validate(destination, message) {
499					Err(SendError::NotApplicable) => None,
500					Err(e) => { return Err(e) },
501					Ok((v, c)) => {
502						maybe_cost = Some(c);
503						Some(v)
504					},
505				}
506			}
507		),* });
508		if let Some(cost) = maybe_cost {
509			Ok((one_ticket, cost))
510		} else {
511			Err(SendError::NotApplicable)
512		}
513	}
514
515	fn deliver(one_ticket: Self::Ticket) -> result::Result<XcmHash, SendError> {
516		for_tuples!( #(
517			if let Some(validated) = one_ticket.Tuple {
518				return Tuple::deliver(validated);
519			}
520		)* );
521		Err(SendError::Unroutable)
522	}
523
524	#[cfg(feature = "runtime-benchmarks")]
525	fn ensure_successful_delivery(location: Option<Location>) {
526		for_tuples!( #(
527			return Tuple::ensure_successful_delivery(location.clone());
528		)* );
529	}
530}
531
532/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
533/// both in `Some` before passing them as mutable references into `T::send_xcm`.
534pub fn validate_send<T: SendXcm>(dest: Location, msg: Xcm<()>) -> SendResult<T::Ticket> {
535	T::validate(&mut Some(dest), &mut Some(msg))
536}
537
538/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
539/// both in `Some` before passing them as mutable references into `T::send_xcm`.
540///
541/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message
542/// could not be sent.
543///
544/// Generally you'll want to validate and get the price first to ensure that the sender can pay it
545/// before actually doing the delivery.
546pub fn send_xcm<T: SendXcm>(
547	dest: Location,
548	msg: Xcm<()>,
549) -> result::Result<(XcmHash, Assets), SendError> {
550	let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?;
551	let hash = T::deliver(ticket)?;
552	Ok((hash, price))
553}