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