referrerpolicy=no-referrer-when-downgrade

staging_xcm/
lib.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
19// NOTE, this crate is meant to be used in many different environments, notably wasm, but not
20// necessarily related to FRAME or even Substrate.
21//
22// Hence, `no_std` rather than sp-runtime.
23#![cfg_attr(not(feature = "std"), no_std)]
24
25extern crate alloc;
26
27use codec::{
28	Decode, DecodeLimit, DecodeWithMemTracking, Encode, Error as CodecError, Input, MaxEncodedLen,
29};
30use derive_where::derive_where;
31use frame_support::dispatch::GetDispatchInfo;
32use scale_info::TypeInfo;
33
34pub mod v3;
35pub mod v4;
36pub mod v5;
37
38pub mod lts {
39	pub use super::v4::*;
40}
41
42pub mod latest {
43	pub use super::v5::*;
44}
45
46mod double_encoded;
47pub use double_encoded::DoubleEncoded;
48
49mod utils;
50
51#[cfg(test)]
52mod tests;
53
54/// Maximum nesting level for XCM decoding.
55pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
56/// The maximal number of instructions in an XCM before decoding fails.
57///
58/// This is a deliberate limit - not a technical one.
59pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100;
60
61/// A version of XCM.
62pub type Version = u32;
63
64#[derive(Clone, Eq, PartialEq, Debug)]
65pub enum Unsupported {}
66impl Encode for Unsupported {}
67impl Decode for Unsupported {
68	fn decode<I: Input>(_: &mut I) -> Result<Self, CodecError> {
69		Err("Not decodable".into())
70	}
71}
72
73/// Attempt to convert `self` into a particular version of itself.
74pub trait IntoVersion: Sized {
75	/// Consume `self` and return same value expressed in some particular `version` of XCM.
76	fn into_version(self, version: Version) -> Result<Self, ()>;
77
78	/// Consume `self` and return same value expressed the latest version of XCM.
79	fn into_latest(self) -> Result<Self, ()> {
80		self.into_version(latest::VERSION)
81	}
82}
83
84pub trait TryAs<T> {
85	fn try_as(&self) -> Result<&T, ()>;
86}
87
88// Macro that generated versioned wrapper types.
89// NOTE: converting a v4 type into a versioned type will make it v5.
90macro_rules! versioned_type {
91	($(#[$attr:meta])* pub enum $n:ident {
92		$(#[$index3:meta])+
93		V3($v3:ty),
94		$(#[$index4:meta])+
95		V4($v4:ty),
96		$(#[$index5:meta])+
97		V5($v5:ty),
98	}) => {
99		#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
100		#[codec(encode_bound())]
101		#[codec(decode_bound())]
102		#[scale_info(replace_segment("staging_xcm", "xcm"))]
103		$(#[$attr])*
104		pub enum $n {
105			$(#[$index3])*
106			V3($v3),
107			$(#[$index4])*
108			V4($v4),
109			$(#[$index5])*
110			V5($v5),
111		}
112		impl $n {
113			pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
114				<Self as TryAs<T>>::try_as(&self)
115			}
116		}
117		impl TryAs<$v3> for $n {
118			fn try_as(&self) -> Result<&$v3, ()> {
119				match &self {
120					Self::V3(ref x) => Ok(x),
121					_ => Err(()),
122				}
123			}
124		}
125		impl TryAs<$v4> for $n {
126			fn try_as(&self) -> Result<&$v4, ()> {
127				match &self {
128					Self::V4(ref x) => Ok(x),
129					_ => Err(()),
130				}
131			}
132		}
133		impl TryAs<$v5> for $n {
134			fn try_as(&self) -> Result<&$v5, ()> {
135				match &self {
136					Self::V5(ref x) => Ok(x),
137					_ => Err(()),
138				}
139			}
140		}
141		impl IntoVersion for $n {
142			fn into_version(self, n: Version) -> Result<Self, ()> {
143				let version = self.identify_version();
144				if version == n {
145					Ok(self)
146				} else {
147					Ok(match n {
148						3 => Self::V3(self.try_into()?),
149						4 => Self::V4(self.try_into()?),
150						5 => Self::V5(self.try_into()?),
151						_ => return Err(()),
152					})
153				}
154			}
155		}
156		impl From<$v3> for $n {
157			fn from(x: $v3) -> Self {
158				$n::V3(x.into())
159			}
160		}
161		impl<T: Into<$v5>> From<T> for $n {
162			fn from(x: T) -> Self {
163				$n::V5(x.into())
164			}
165		}
166		impl TryFrom<$n> for $v3 {
167			type Error = ();
168			fn try_from(x: $n) -> Result<Self, ()> {
169				use $n::*;
170				match x {
171					V3(x) => Ok(x),
172					V4(x) => x.try_into().map_err(|_| ()),
173					V5(x) => {
174						let v4: $v4 = x.try_into().map_err(|_| ())?;
175						v4.try_into().map_err(|_| ())
176					}
177				}
178			}
179		}
180		impl TryFrom<$n> for $v4 {
181			type Error = ();
182			fn try_from(x: $n) -> Result<Self, ()> {
183				use $n::*;
184				match x {
185					V3(x) => x.try_into().map_err(|_| ()),
186					V4(x) => Ok(x),
187					V5(x) => x.try_into().map_err(|_| ()),
188				}
189			}
190		}
191		impl TryFrom<$n> for $v5 {
192			type Error = ();
193			fn try_from(x: $n) -> Result<Self, ()> {
194				use $n::*;
195				match x {
196					V3(x) => {
197						let v4: $v4 = x.try_into().map_err(|_| ())?;
198						v4.try_into().map_err(|_| ())
199					},
200					V4(x) => x.try_into().map_err(|_| ()),
201					V5(x) => Ok(x),
202				}
203			}
204		}
205		impl MaxEncodedLen for $n {
206			fn max_encoded_len() -> usize {
207				<$v3>::max_encoded_len()
208			}
209		}
210		impl IdentifyVersion for $n {
211			fn identify_version(&self) -> Version {
212				use $n::*;
213				match self {
214					V3(_) => v3::VERSION,
215					V4(_) => v4::VERSION,
216					V5(_) => v5::VERSION,
217				}
218			}
219		}
220	};
221}
222
223versioned_type! {
224	/// A single version's `AssetId` value, together with its version code.
225	pub enum VersionedAssetId {
226		#[codec(index = 3)]
227		V3(v3::AssetId),
228		#[codec(index = 4)]
229		V4(v4::AssetId),
230		#[codec(index = 5)]
231		V5(v5::AssetId),
232	}
233}
234
235versioned_type! {
236	/// A single version's `Response` value, together with its version code.
237	pub enum VersionedResponse {
238		#[codec(index = 3)]
239		V3(v3::Response),
240		#[codec(index = 4)]
241		V4(v4::Response),
242		#[codec(index = 5)]
243		V5(v5::Response),
244	}
245}
246
247versioned_type! {
248	/// A single `NetworkId` value, together with its version code.
249	pub enum VersionedNetworkId {
250		#[codec(index = 3)]
251		V3(v3::NetworkId),
252		#[codec(index = 4)]
253		V4(v4::NetworkId),
254		#[codec(index = 5)]
255		V5(v5::NetworkId),
256	}
257}
258
259versioned_type! {
260	/// A single `Junction` value, together with its version code.
261	pub enum VersionedJunction {
262		#[codec(index = 3)]
263		V3(v3::Junction),
264		#[codec(index = 4)]
265		V4(v4::Junction),
266		#[codec(index = 5)]
267		V5(v5::Junction),
268	}
269}
270
271versioned_type! {
272	/// A single `Location` value, together with its version code.
273	#[derive(Ord, PartialOrd)]
274	pub enum VersionedLocation {
275		#[codec(index = 3)]
276		V3(v3::MultiLocation),
277		#[codec(index = 4)]
278		V4(v4::Location),
279		#[codec(index = 5)]
280		V5(v5::Location),
281	}
282}
283
284versioned_type! {
285	/// A single `InteriorLocation` value, together with its version code.
286	pub enum VersionedInteriorLocation {
287		#[codec(index = 3)]
288		V3(v3::InteriorMultiLocation),
289		#[codec(index = 4)]
290		V4(v4::InteriorLocation),
291		#[codec(index = 5)]
292		V5(v5::InteriorLocation),
293	}
294}
295
296versioned_type! {
297	/// A single `Asset` value, together with its version code.
298	pub enum VersionedAsset {
299		#[codec(index = 3)]
300		V3(v3::MultiAsset),
301		#[codec(index = 4)]
302		V4(v4::Asset),
303		#[codec(index = 5)]
304		V5(v5::Asset),
305	}
306}
307
308versioned_type! {
309	/// A single `MultiAssets` value, together with its version code.
310	pub enum VersionedAssets {
311		#[codec(index = 3)]
312		V3(v3::MultiAssets),
313		#[codec(index = 4)]
314		V4(v4::Assets),
315		#[codec(index = 5)]
316		V5(v5::Assets),
317	}
318}
319
320/// A single XCM message, together with its version code.
321#[derive(Encode, Decode, DecodeWithMemTracking, TypeInfo)]
322#[derive_where(Clone, Eq, PartialEq, Debug)]
323#[codec(encode_bound())]
324#[codec(decode_bound())]
325#[scale_info(bounds(), skip_type_params(RuntimeCall))]
326#[scale_info(replace_segment("staging_xcm", "xcm"))]
327pub enum VersionedXcm<RuntimeCall> {
328	#[codec(index = 3)]
329	V3(v3::Xcm<RuntimeCall>),
330	#[codec(index = 4)]
331	V4(v4::Xcm<RuntimeCall>),
332	#[codec(index = 5)]
333	V5(v5::Xcm<RuntimeCall>),
334}
335
336impl<C: Decode + GetDispatchInfo> IntoVersion for VersionedXcm<C> {
337	fn into_version(self, n: Version) -> Result<Self, ()> {
338		Ok(match n {
339			3 => Self::V3(self.try_into()?),
340			4 => Self::V4(self.try_into()?),
341			5 => Self::V5(self.try_into()?),
342			_ => return Err(()),
343		})
344	}
345}
346
347impl<C> IdentifyVersion for VersionedXcm<C> {
348	fn identify_version(&self) -> Version {
349		match self {
350			Self::V3(_) => v3::VERSION,
351			Self::V4(_) => v4::VERSION,
352			Self::V5(_) => v5::VERSION,
353		}
354	}
355}
356
357impl<C> VersionedXcm<C> {
358	/// Checks if the XCM is decodable. Consequently, it checks all decoding constraints,
359	/// such as `MAX_XCM_DECODE_DEPTH`, `MAX_ITEMS_IN_ASSETS` or `MAX_INSTRUCTIONS_TO_DECODE`.
360	///
361	/// Note that this uses the limit of the sender - not the receiver. It is a best effort.
362	pub fn check_is_decodable(&self) -> Result<(), ()> {
363		self.using_encoded(|mut enc| {
364			Self::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut enc).map(|_| ())
365		})
366		.map_err(|e| {
367			tracing::error!(target: "xcm::check_is_decodable", error=?e, xcm=?self, "Decode error!");
368			()
369		})
370	}
371}
372
373impl<RuntimeCall> From<v3::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
374	fn from(x: v3::Xcm<RuntimeCall>) -> Self {
375		VersionedXcm::V3(x)
376	}
377}
378
379impl<RuntimeCall> From<v4::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
380	fn from(x: v4::Xcm<RuntimeCall>) -> Self {
381		VersionedXcm::V4(x)
382	}
383}
384
385impl<RuntimeCall> From<v5::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
386	fn from(x: v5::Xcm<RuntimeCall>) -> Self {
387		VersionedXcm::V5(x)
388	}
389}
390
391impl<Call: Decode + GetDispatchInfo> TryFrom<VersionedXcm<Call>> for v3::Xcm<Call> {
392	type Error = ();
393	fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
394		use VersionedXcm::*;
395		match x {
396			V3(x) => Ok(x),
397			V4(x) => x.try_into(),
398			V5(x) => {
399				let v4: v4::Xcm<Call> = x.try_into()?;
400				v4.try_into()
401			},
402		}
403	}
404}
405
406impl<Call: Decode + GetDispatchInfo> TryFrom<VersionedXcm<Call>> for v4::Xcm<Call> {
407	type Error = ();
408	fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
409		use VersionedXcm::*;
410		match x {
411			V3(x) => x.try_into(),
412			V4(x) => Ok(x),
413			V5(x) => x.try_into(),
414		}
415	}
416}
417
418impl<Call: Decode + GetDispatchInfo> TryFrom<VersionedXcm<Call>> for v5::Xcm<Call> {
419	type Error = ();
420	fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
421		use VersionedXcm::*;
422		match x {
423			V3(x) => {
424				let v4: v4::Xcm<Call> = x.try_into()?;
425				v4.try_into()
426			},
427			V4(x) => x.try_into(),
428			V5(x) => Ok(x),
429		}
430	}
431}
432
433/// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `Location` which will
434/// interpret it.
435pub trait WrapVersion {
436	fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
437		dest: &latest::Location,
438		xcm: impl Into<VersionedXcm<RuntimeCall>>,
439	) -> Result<VersionedXcm<RuntimeCall>, ()>;
440}
441
442/// Used to get the version out of a versioned type.
443// TODO(XCMv5): This could be `GetVersion` and we change the current one to `GetVersionFor`.
444pub trait IdentifyVersion {
445	fn identify_version(&self) -> Version;
446}
447
448/// Check and return the `Version` that should be used for the `Xcm` datum for the destination
449/// `Location`, which will interpret it.
450pub trait GetVersion {
451	fn get_version_for(dest: &latest::Location) -> Option<Version>;
452}
453
454/// `()` implementation does nothing with the XCM, just sending with whatever version it was
455/// authored as.
456impl WrapVersion for () {
457	fn wrap_version<RuntimeCall>(
458		_: &latest::Location,
459		xcm: impl Into<VersionedXcm<RuntimeCall>>,
460	) -> Result<VersionedXcm<RuntimeCall>, ()> {
461		Ok(xcm.into())
462	}
463}
464
465/// `WrapVersion` implementation which attempts to always convert the XCM to version 3 before
466/// wrapping it.
467pub struct AlwaysV3;
468impl WrapVersion for AlwaysV3 {
469	fn wrap_version<Call: Decode + GetDispatchInfo>(
470		_: &latest::Location,
471		xcm: impl Into<VersionedXcm<Call>>,
472	) -> Result<VersionedXcm<Call>, ()> {
473		Ok(VersionedXcm::<Call>::V3(xcm.into().try_into()?))
474	}
475}
476impl GetVersion for AlwaysV3 {
477	fn get_version_for(_dest: &latest::Location) -> Option<Version> {
478		Some(v3::VERSION)
479	}
480}
481
482/// `WrapVersion` implementation which attempts to always convert the XCM to version 4 before
483/// wrapping it.
484pub struct AlwaysV4;
485impl WrapVersion for AlwaysV4 {
486	fn wrap_version<Call: Decode + GetDispatchInfo>(
487		_: &latest::Location,
488		xcm: impl Into<VersionedXcm<Call>>,
489	) -> Result<VersionedXcm<Call>, ()> {
490		Ok(VersionedXcm::<Call>::V4(xcm.into().try_into()?))
491	}
492}
493impl GetVersion for AlwaysV4 {
494	fn get_version_for(_dest: &latest::Location) -> Option<Version> {
495		Some(v4::VERSION)
496	}
497}
498
499/// `WrapVersion` implementation which attempts to always convert the XCM to version 5 before
500/// wrapping it.
501pub struct AlwaysV5;
502impl WrapVersion for AlwaysV5 {
503	fn wrap_version<Call: Decode + GetDispatchInfo>(
504		_: &latest::Location,
505		xcm: impl Into<VersionedXcm<Call>>,
506	) -> Result<VersionedXcm<Call>, ()> {
507		Ok(VersionedXcm::<Call>::V5(xcm.into().try_into()?))
508	}
509}
510impl GetVersion for AlwaysV5 {
511	fn get_version_for(_dest: &latest::Location) -> Option<Version> {
512		Some(v5::VERSION)
513	}
514}
515
516/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version
517/// before wrapping it.
518pub type AlwaysLatest = AlwaysV5;
519
520/// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long-
521/// Term-Support version before wrapping it.
522pub type AlwaysLts = AlwaysV4;
523
524pub mod prelude {
525	pub use super::{
526		latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV3, AlwaysV4, AlwaysV5, GetVersion,
527		IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset,
528		VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation,
529		VersionedResponse, VersionedXcm, WrapVersion,
530	};
531
532	/// The minimal supported XCM version
533	pub const MIN_XCM_VERSION: XcmVersion = 3;
534}
535
536pub mod opaque {
537	pub mod v3 {
538		// Everything from v3
539		pub use crate::v3::*;
540		// Then override with the opaque types in v3
541		pub use crate::v3::opaque::{Instruction, Xcm};
542	}
543	pub mod v4 {
544		// Everything from v4
545		pub use crate::v4::*;
546		// Then override with the opaque types in v4
547		pub use crate::v4::opaque::{Instruction, Xcm};
548	}
549	pub mod v5 {
550		// Everything from v4
551		pub use crate::v5::*;
552		// Then override with the opaque types in v5
553		pub use crate::v5::opaque::{Instruction, Xcm};
554	}
555
556	pub mod latest {
557		pub use super::v5::*;
558	}
559
560	pub mod lts {
561		pub use super::v4::*;
562	}
563
564	/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
565	pub type VersionedXcm = super::VersionedXcm<()>;
566}
567
568#[test]
569fn conversion_works() {
570	use latest::prelude::*;
571	let assets: Assets = (Here, 1u128).into();
572	let _: VersionedAssets = assets.into();
573}
574
575#[test]
576fn size_limits() {
577	extern crate std;
578
579	let mut test_failed = false;
580	macro_rules! check_sizes {
581        ($(($kind:ty, $expected:expr),)+) => {
582            $({
583                let s = core::mem::size_of::<$kind>();
584                // Since the types often affect the size of other types in which they're included
585                // it is more convenient to check multiple types at the same time and only fail
586                // the test at the end. For debugging it's also useful to print out all of the sizes,
587                // even if they're within the expected range.
588                if s > $expected {
589                    test_failed = true;
590                    std::eprintln!(
591                        "assertion failed: size of '{}' is {} (which is more than the expected {})",
592                        stringify!($kind),
593                        s,
594                        $expected
595                    );
596                } else {
597                    std::println!(
598                        "type '{}' is of size {} which is within the expected {}",
599                        stringify!($kind),
600                        s,
601                        $expected
602                    );
603                }
604            })+
605        }
606    }
607
608	check_sizes! {
609		(crate::latest::Instruction<()>, 128),
610		(crate::latest::Asset, 80),
611		(crate::latest::Location, 24),
612		(crate::latest::AssetId, 40),
613		(crate::latest::Junctions, 16),
614		(crate::latest::Junction, 88),
615		(crate::latest::Response, 40),
616		(crate::latest::AssetInstance, 48),
617		(crate::latest::NetworkId, 48),
618		(crate::latest::BodyId, 32),
619		(crate::latest::Assets, 24),
620		(crate::latest::BodyPart, 12),
621	}
622	assert!(!test_failed);
623}
624
625#[test]
626fn check_is_decodable_works() {
627	use crate::{
628		latest::{
629			prelude::{GeneralIndex, ReserveAssetDeposited, SetAppendix},
630			Assets, Xcm, MAX_ITEMS_IN_ASSETS,
631		},
632		MAX_INSTRUCTIONS_TO_DECODE,
633	};
634
635	// closure generates assets of `count`
636	let assets = |count| {
637		let mut assets = Assets::new();
638		for i in 0..count {
639			assets.push((GeneralIndex(i as u128), 100).into());
640		}
641		assets
642	};
643
644	// closer generates `Xcm` with nested instructions of `depth`
645	let with_instr = |depth| {
646		let mut xcm = Xcm::<()>(vec![]);
647		for _ in 0..depth - 1 {
648			xcm = Xcm::<()>(vec![SetAppendix(xcm)]);
649		}
650		xcm
651	};
652
653	// `MAX_INSTRUCTIONS_TO_DECODE` check
654	assert!(VersionedXcm::<()>::from(Xcm(vec![
655		ReserveAssetDeposited(assets(1));
656		(MAX_INSTRUCTIONS_TO_DECODE - 1) as usize
657	]))
658	.check_is_decodable()
659	.is_ok());
660	assert!(VersionedXcm::<()>::from(Xcm(vec![
661		ReserveAssetDeposited(assets(1));
662		MAX_INSTRUCTIONS_TO_DECODE as usize
663	]))
664	.check_is_decodable()
665	.is_ok());
666	assert!(VersionedXcm::<()>::from(Xcm(vec![
667		ReserveAssetDeposited(assets(1));
668		(MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
669	]))
670	.check_is_decodable()
671	.is_err());
672
673	// `MAX_XCM_DECODE_DEPTH` check
674	assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH - 1))
675		.check_is_decodable()
676		.is_ok());
677	assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH))
678		.check_is_decodable()
679		.is_ok());
680	assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH + 1))
681		.check_is_decodable()
682		.is_err());
683
684	// `MAX_ITEMS_IN_ASSETS` check
685	assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
686		MAX_ITEMS_IN_ASSETS
687	))]))
688	.check_is_decodable()
689	.is_ok());
690	assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
691		MAX_ITEMS_IN_ASSETS - 1
692	))]))
693	.check_is_decodable()
694	.is_ok());
695	assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
696		MAX_ITEMS_IN_ASSETS + 1
697	))]))
698	.check_is_decodable()
699	.is_err());
700}