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}