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