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