staging_xcm/v2/
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
19use crate::v3::Error as NewError;
20use codec::{Decode, Encode};
21use core::result;
22use scale_info::TypeInfo;
23
24use super::*;
25
26// A simple trait to get the weight of some object.
27pub trait GetWeight<W> {
28	fn weight(&self) -> sp_weights::Weight;
29}
30
31#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
32#[scale_info(replace_segment("staging_xcm", "xcm"))]
33pub enum Error {
34	// Errors that happen due to instructions being executed. These alone are defined in the
35	// XCM specification.
36	/// An arithmetic overflow happened.
37	#[codec(index = 0)]
38	Overflow,
39	/// The instruction is intentionally unsupported.
40	#[codec(index = 1)]
41	Unimplemented,
42	/// Origin Register does not contain a value value for a reserve transfer notification.
43	#[codec(index = 2)]
44	UntrustedReserveLocation,
45	/// Origin Register does not contain a value value for a teleport notification.
46	#[codec(index = 3)]
47	UntrustedTeleportLocation,
48	/// `MultiLocation` value too large to descend further.
49	#[codec(index = 4)]
50	MultiLocationFull,
51	/// `MultiLocation` value ascend more parents than known ancestors of local location.
52	#[codec(index = 5)]
53	MultiLocationNotInvertible,
54	/// The Origin Register does not contain a valid value for instruction.
55	#[codec(index = 6)]
56	BadOrigin,
57	/// The location parameter is not a valid value for the instruction.
58	#[codec(index = 7)]
59	InvalidLocation,
60	/// The given asset is not handled.
61	#[codec(index = 8)]
62	AssetNotFound,
63	/// An asset transaction (like withdraw or deposit) failed (typically due to type conversions).
64	#[codec(index = 9)]
65	FailedToTransactAsset(#[codec(skip)] &'static str),
66	/// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights.
67	#[codec(index = 10)]
68	NotWithdrawable,
69	/// An asset cannot be deposited under the ownership of a particular location.
70	#[codec(index = 11)]
71	LocationCannotHold,
72	/// Attempt to send a message greater than the maximum supported by the transport protocol.
73	#[codec(index = 12)]
74	ExceedsMaxMessageSize,
75	/// The given message cannot be translated into a format supported by the destination.
76	#[codec(index = 13)]
77	DestinationUnsupported,
78	/// Destination is routable, but there is some issue with the transport mechanism.
79	#[codec(index = 14)]
80	Transport(#[codec(skip)] &'static str),
81	/// Destination is known to be unroutable.
82	#[codec(index = 15)]
83	Unroutable,
84	/// Used by `ClaimAsset` when the given claim could not be recognized/found.
85	#[codec(index = 16)]
86	UnknownClaim,
87	/// Used by `Transact` when the functor cannot be decoded.
88	#[codec(index = 17)]
89	FailedToDecode,
90	/// Used by `Transact` to indicate that the given weight limit could be breached by the
91	/// functor.
92	#[codec(index = 18)]
93	MaxWeightInvalid,
94	/// Used by `BuyExecution` when the Holding Register does not contain payable fees.
95	#[codec(index = 19)]
96	NotHoldingFees,
97	/// Used by `BuyExecution` when the fees declared to purchase weight are insufficient.
98	#[codec(index = 20)]
99	TooExpensive,
100	/// Used by the `Trap` instruction to force an error intentionally. Its code is included.
101	#[codec(index = 21)]
102	Trap(u64),
103
104	// Errors that happen prior to instructions being executed. These fall outside of the XCM
105	// spec.
106	/// XCM version not able to be handled.
107	UnhandledXcmVersion,
108	/// Execution of the XCM would potentially result in a greater weight used than weight limit.
109	WeightLimitReached(Weight),
110	/// The XCM did not pass the barrier condition for execution.
111	///
112	/// The barrier condition differs on different chains and in different circumstances, but
113	/// generally it means that the conditions surrounding the message were not such that the chain
114	/// considers the message worth spending time executing. Since most chains lift the barrier to
115	/// execution on appropriate payment, presentation of an NFT voucher, or based on the message
116	/// origin, it means that none of those were the case.
117	Barrier,
118	/// The weight of an XCM message is not computable ahead of execution.
119	WeightNotComputable,
120}
121
122impl TryFrom<NewError> for Error {
123	type Error = ();
124	fn try_from(new_error: NewError) -> result::Result<Error, ()> {
125		use NewError::*;
126		Ok(match new_error {
127			Overflow => Self::Overflow,
128			Unimplemented => Self::Unimplemented,
129			UntrustedReserveLocation => Self::UntrustedReserveLocation,
130			UntrustedTeleportLocation => Self::UntrustedTeleportLocation,
131			LocationFull => Self::MultiLocationFull,
132			LocationNotInvertible => Self::MultiLocationNotInvertible,
133			BadOrigin => Self::BadOrigin,
134			InvalidLocation => Self::InvalidLocation,
135			AssetNotFound => Self::AssetNotFound,
136			FailedToTransactAsset(s) => Self::FailedToTransactAsset(s),
137			NotWithdrawable => Self::NotWithdrawable,
138			LocationCannotHold => Self::LocationCannotHold,
139			ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize,
140			DestinationUnsupported => Self::DestinationUnsupported,
141			Transport(s) => Self::Transport(s),
142			Unroutable => Self::Unroutable,
143			UnknownClaim => Self::UnknownClaim,
144			FailedToDecode => Self::FailedToDecode,
145			MaxWeightInvalid => Self::MaxWeightInvalid,
146			NotHoldingFees => Self::NotHoldingFees,
147			TooExpensive => Self::TooExpensive,
148			Trap(i) => Self::Trap(i),
149			_ => return Err(()),
150		})
151	}
152}
153
154impl From<SendError> for Error {
155	fn from(e: SendError) -> Self {
156		match e {
157			SendError::NotApplicable(..) | SendError::Unroutable => Error::Unroutable,
158			SendError::Transport(s) => Error::Transport(s),
159			SendError::DestinationUnsupported => Error::DestinationUnsupported,
160			SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
161		}
162	}
163}
164
165pub type Result = result::Result<(), Error>;
166
167/// Outcome of an XCM execution.
168#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
169#[scale_info(replace_segment("staging_xcm", "xcm"))]
170pub enum Outcome {
171	/// Execution completed successfully; given weight was used.
172	Complete(Weight),
173	/// Execution started, but did not complete successfully due to the given error; given weight
174	/// was used.
175	Incomplete(Weight, Error),
176	/// Execution did not start due to the given error.
177	Error(Error),
178}
179
180impl Outcome {
181	pub fn ensure_complete(self) -> Result {
182		match self {
183			Outcome::Complete(_) => Ok(()),
184			Outcome::Incomplete(_, e) => Err(e),
185			Outcome::Error(e) => Err(e),
186		}
187	}
188	pub fn ensure_execution(self) -> result::Result<Weight, Error> {
189		match self {
190			Outcome::Complete(w) => Ok(w),
191			Outcome::Incomplete(w, _) => Ok(w),
192			Outcome::Error(e) => Err(e),
193		}
194	}
195	/// How much weight was used by the XCM execution attempt.
196	pub fn weight_used(&self) -> Weight {
197		match self {
198			Outcome::Complete(w) => *w,
199			Outcome::Incomplete(w, _) => *w,
200			Outcome::Error(_) => 0,
201		}
202	}
203}
204
205/// Type of XCM message executor.
206pub trait ExecuteXcm<RuntimeCall> {
207	/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The
208	/// weight limit is a basic hard-limit and the implementation may place further restrictions or
209	/// requirements on weight and other aspects.
210	fn execute_xcm(
211		origin: impl Into<MultiLocation>,
212		message: Xcm<RuntimeCall>,
213		weight_limit: Weight,
214	) -> Outcome {
215		let origin = origin.into();
216		log::debug!(
217			target: "xcm::execute_xcm",
218			"origin: {:?}, message: {:?}, weight_limit: {:?}",
219			origin,
220			message,
221			weight_limit,
222		);
223		Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
224	}
225
226	/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight.
227	///
228	/// Some amount of `weight_credit` may be provided which, depending on the implementation, may
229	/// allow execution without associated payment.
230	fn execute_xcm_in_credit(
231		origin: impl Into<MultiLocation>,
232		message: Xcm<RuntimeCall>,
233		weight_limit: Weight,
234		weight_credit: Weight,
235	) -> Outcome;
236}
237
238impl<C> ExecuteXcm<C> for () {
239	fn execute_xcm_in_credit(
240		_origin: impl Into<MultiLocation>,
241		_message: Xcm<C>,
242		_weight_limit: Weight,
243		_weight_credit: Weight,
244	) -> Outcome {
245		Outcome::Error(Error::Unimplemented)
246	}
247}
248
249/// Error result value when attempting to send an XCM message.
250#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
251#[scale_info(replace_segment("staging_xcm", "xcm"))]
252pub enum SendError {
253	/// The message and destination combination was not recognized as being reachable.
254	///
255	/// This is not considered fatal: if there are alternative transport routes available, then
256	/// they may be attempted. For this reason, the destination and message are contained.
257	NotApplicable(MultiLocation, Xcm<()>),
258	/// Destination is routable, but there is some issue with the transport mechanism. This is
259	/// considered fatal.
260	/// A human-readable explanation of the specific issue is provided.
261	Transport(#[codec(skip)] &'static str),
262	/// Destination is known to be unroutable. This is considered fatal.
263	Unroutable,
264	/// The given message cannot be translated into a format that the destination can be expected
265	/// to interpret.
266	DestinationUnsupported,
267	/// Message could not be sent due to its size exceeding the maximum allowed by the transport
268	/// layer.
269	ExceedsMaxMessageSize,
270}
271
272/// Result value when attempting to send an XCM message.
273pub type SendResult = result::Result<(), SendError>;
274
275/// Utility for sending an XCM message.
276///
277/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each
278/// router might return `NotApplicable` to pass the execution to the next sender item. Note that
279/// each `NotApplicable` might alter the destination and the XCM message for to the next router.
280///
281///
282/// # Example
283/// ```rust
284/// # use staging_xcm::v2::prelude::*;
285/// # use codec::Encode;
286///
287/// /// A sender that only passes the message through and does nothing.
288/// struct Sender1;
289/// impl SendXcm for Sender1 {
290///     fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult {
291///         return Err(SendError::NotApplicable(destination.into(), message))
292///     }
293/// }
294///
295/// /// A sender that accepts a message that has two junctions, otherwise stops the routing.
296/// struct Sender2;
297/// impl SendXcm for Sender2 {
298///     fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult {
299///         let destination = destination.into();
300///         if destination.parents == 0 && destination.interior.len() == 2 {
301///             Ok(())
302///         } else {
303///             Err(SendError::Unroutable)
304///         }
305///     }
306/// }
307///
308/// /// A sender that accepts a message from a parent, passing through otherwise.
309/// struct Sender3;
310/// impl SendXcm for Sender3 {
311///     fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult {
312///         let destination = destination.into();
313///         match destination {
314///             MultiLocation { parents: 1, interior: Here } => Ok(()),
315///             _ => Err(SendError::NotApplicable(destination, message)),
316///         }
317///     }
318/// }
319///
320/// // A call to send via XCM. We don't really care about this.
321/// # fn main() {
322/// let call: Vec<u8> = ().encode();
323/// let message = Xcm(vec![Instruction::Transact {
324///     origin_type: OriginKind::Superuser,
325///     require_weight_at_most: 0,
326///     call: call.into(),
327/// }]);
328///
329/// assert!(
330///     // Sender2 will block this.
331///     <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(Parent, message.clone())
332///         .is_err()
333/// );
334///
335/// assert!(
336///     // Sender3 will catch this.
337///     <(Sender1, Sender3) as SendXcm>::send_xcm(Parent, message.clone())
338///         .is_ok()
339/// );
340/// # }
341/// ```
342pub trait SendXcm {
343	/// Send an XCM `message` to a given `destination`.
344	///
345	/// If it is not a destination which can be reached with this type but possibly could by others,
346	/// then it *MUST* return `NotApplicable`. Any other error will cause the tuple implementation
347	/// to exit early without trying other type fields.
348	fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult;
349}
350
351#[impl_trait_for_tuples::impl_for_tuples(30)]
352impl SendXcm for Tuple {
353	fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult {
354		for_tuples!( #(
355			// we shadow `destination` and `message` in each expansion for the next one.
356			let (destination, message) = match Tuple::send_xcm(destination, message) {
357				Err(SendError::NotApplicable(d, m)) => (d, m),
358				o @ _ => return o,
359			};
360		)* );
361		Err(SendError::NotApplicable(destination.into(), message))
362	}
363}