1#![cfg_attr(not(feature = "std"), no_std)]
24#![allow(deprecated)]
26
27extern crate alloc;
28
29use codec::{Decode, DecodeLimit, Encode, Error as CodecError, Input, MaxEncodedLen};
30use derivative::Derivative;
31use scale_info::TypeInfo;
32
33#[deprecated(
34 note = "XCMv2 will be removed once XCMv5 is released. Please use XCMv3 or XCMv4 instead."
35)]
36pub mod v2;
37pub mod v3;
38pub mod v4;
39
40pub mod lts {
41 pub use super::v4::*;
42}
43
44pub mod latest {
45 pub use super::v4::*;
46}
47
48mod double_encoded;
49pub use double_encoded::DoubleEncoded;
50
51#[cfg(test)]
52mod tests;
53
54pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
56
57pub type Version = u32;
59
60#[derive(Clone, Eq, PartialEq, Debug)]
61pub enum Unsupported {}
62impl Encode for Unsupported {}
63impl Decode for Unsupported {
64 fn decode<I: Input>(_: &mut I) -> Result<Self, CodecError> {
65 Err("Not decodable".into())
66 }
67}
68
69pub trait IntoVersion: Sized {
71 fn into_version(self, version: Version) -> Result<Self, ()>;
73
74 fn into_latest(self) -> Result<Self, ()> {
76 self.into_version(latest::VERSION)
77 }
78}
79
80pub trait TryAs<T> {
81 fn try_as(&self) -> Result<&T, ()>;
82}
83
84macro_rules! versioned_type {
85 ($(#[$attr:meta])* pub enum $n:ident {
86 $(#[$index3:meta])+
87 V3($v3:ty),
88 $(#[$index4:meta])+
89 V4($v4:ty),
90 }) => {
91 #[derive(Derivative, Encode, Decode, TypeInfo)]
92 #[derivative(
93 Clone(bound = ""),
94 Eq(bound = ""),
95 PartialEq(bound = ""),
96 Debug(bound = "")
97 )]
98 #[codec(encode_bound())]
99 #[codec(decode_bound())]
100 #[scale_info(replace_segment("staging_xcm", "xcm"))]
101 $(#[$attr])*
102 pub enum $n {
103 $(#[$index3])*
104 V3($v3),
105 $(#[$index4])*
106 V4($v4),
107 }
108 impl $n {
109 pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
110 <Self as TryAs<T>>::try_as(&self)
111 }
112 }
113 impl TryAs<$v3> for $n {
114 fn try_as(&self) -> Result<&$v3, ()> {
115 match &self {
116 Self::V3(ref x) => Ok(x),
117 _ => Err(()),
118 }
119 }
120 }
121 impl TryAs<$v4> for $n {
122 fn try_as(&self) -> Result<&$v4, ()> {
123 match &self {
124 Self::V4(ref x) => Ok(x),
125 _ => Err(()),
126 }
127 }
128 }
129 impl IntoVersion for $n {
130 fn into_version(self, n: Version) -> Result<Self, ()> {
131 Ok(match n {
132 3 => Self::V3(self.try_into()?),
133 4 => Self::V4(self.try_into()?),
134 _ => return Err(()),
135 })
136 }
137 }
138 impl From<$v3> for $n {
139 fn from(x: $v3) -> Self {
140 $n::V3(x.into())
141 }
142 }
143 impl From<$v4> for $n {
144 fn from(x: $v4) -> Self {
145 $n::V4(x.into())
146 }
147 }
148 impl TryFrom<$n> for $v3 {
149 type Error = ();
150 fn try_from(x: $n) -> Result<Self, ()> {
151 use $n::*;
152 match x {
153 V3(x) => Ok(x),
154 V4(x) => x.try_into(),
155 }
156 }
157 }
158 impl TryFrom<$n> for $v4 {
159 type Error = ();
160 fn try_from(x: $n) -> Result<Self, ()> {
161 use $n::*;
162 match x {
163 V3(x) => x.try_into().map_err(|_| ()),
164 V4(x) => Ok(x),
165 }
166 }
167 }
168 impl MaxEncodedLen for $n {
169 fn max_encoded_len() -> usize {
170 <$v3>::max_encoded_len()
171 }
172 }
173 impl IdentifyVersion for $n {
174 fn identify_version(&self) -> Version {
175 use $n::*;
176 match self {
177 V3(_) => v3::VERSION,
178 V4(_) => v4::VERSION,
179 }
180 }
181 }
182 };
183
184 ($(#[$attr:meta])* pub enum $n:ident {
185 $(#[$index2:meta])+
186 V2($v2:ty),
187 $(#[$index3:meta])+
188 V3($v3:ty),
189 $(#[$index4:meta])+
190 V4($v4:ty),
191 }) => {
192 #[derive(Derivative, Encode, Decode, TypeInfo)]
193 #[derivative(
194 Clone(bound = ""),
195 Eq(bound = ""),
196 PartialEq(bound = ""),
197 Debug(bound = "")
198 )]
199 #[codec(encode_bound())]
200 #[codec(decode_bound())]
201 #[scale_info(replace_segment("staging_xcm", "xcm"))]
202 $(#[$attr])*
203 pub enum $n {
204 $(#[$index2])*
205 V2($v2),
206 $(#[$index3])*
207 V3($v3),
208 $(#[$index4])*
209 V4($v4),
210 }
211 impl $n {
212 pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
213 <Self as TryAs<T>>::try_as(&self)
214 }
215 }
216 impl TryAs<$v2> for $n {
217 fn try_as(&self) -> Result<&$v2, ()> {
218 match &self {
219 Self::V2(ref x) => Ok(x),
220 _ => Err(()),
221 }
222 }
223 }
224 impl TryAs<$v3> for $n {
225 fn try_as(&self) -> Result<&$v3, ()> {
226 match &self {
227 Self::V3(ref x) => Ok(x),
228 _ => Err(()),
229 }
230 }
231 }
232 impl TryAs<$v4> for $n {
233 fn try_as(&self) -> Result<&$v4, ()> {
234 match &self {
235 Self::V4(ref x) => Ok(x),
236 _ => Err(()),
237 }
238 }
239 }
240 impl IntoVersion for $n {
241 fn into_version(self, n: Version) -> Result<Self, ()> {
242 Ok(match n {
243 1 | 2 => Self::V2(self.try_into()?),
244 3 => Self::V3(self.try_into()?),
245 4 => Self::V4(self.try_into()?),
246 _ => return Err(()),
247 })
248 }
249 }
250 impl From<$v2> for $n {
251 fn from(x: $v2) -> Self {
252 $n::V2(x)
253 }
254 }
255 impl<T: Into<$v4>> From<T> for $n {
256 fn from(x: T) -> Self {
257 $n::V4(x.into())
258 }
259 }
260 impl TryFrom<$n> for $v2 {
261 type Error = ();
262 fn try_from(x: $n) -> Result<Self, ()> {
263 use $n::*;
264 match x {
265 V2(x) => Ok(x),
266 V3(x) => x.try_into(),
267 V4(x) => {
268 let v3: $v3 = x.try_into().map_err(|_| ())?;
269 v3.try_into()
270 },
271 }
272 }
273 }
274 impl TryFrom<$n> for $v3 {
275 type Error = ();
276 fn try_from(x: $n) -> Result<Self, ()> {
277 use $n::*;
278 match x {
279 V2(x) => x.try_into(),
280 V3(x) => Ok(x),
281 V4(x) => x.try_into().map_err(|_| ()),
282 }
283 }
284 }
285 impl TryFrom<$n> for $v4 {
286 type Error = ();
287 fn try_from(x: $n) -> Result<Self, ()> {
288 use $n::*;
289 match x {
290 V2(x) => {
291 let v3: $v3 = x.try_into().map_err(|_| ())?;
292 v3.try_into().map_err(|_| ())
293 },
294 V3(x) => x.try_into().map_err(|_| ()),
295 V4(x) => Ok(x),
296 }
297 }
298 }
299 impl MaxEncodedLen for $n {
300 fn max_encoded_len() -> usize {
301 <$v3>::max_encoded_len()
302 }
303 }
304 impl IdentifyVersion for $n {
305 fn identify_version(&self) -> Version {
306 use $n::*;
307 match self {
308 V2(_) => v2::VERSION,
309 V3(_) => v3::VERSION,
310 V4(_) => v4::VERSION,
311 }
312 }
313 }
314 };
315}
316
317versioned_type! {
318 pub enum VersionedAssetId {
320 #[codec(index = 3)]
321 V3(v3::AssetId),
322 #[codec(index = 4)]
323 V4(v4::AssetId),
324 }
325}
326
327versioned_type! {
328 pub enum VersionedResponse {
330 #[codec(index = 2)]
331 V2(v2::Response),
332 #[codec(index = 3)]
333 V3(v3::Response),
334 #[codec(index = 4)]
335 V4(v4::Response),
336 }
337}
338
339versioned_type! {
340 pub enum VersionedNetworkId {
342 #[codec(index = 2)]
343 V2(v2::NetworkId),
344 #[codec(index = 3)]
345 V3(v3::NetworkId),
346 #[codec(index = 4)]
347 V4(v4::NetworkId),
348 }
349}
350
351versioned_type! {
352 pub enum VersionedJunction {
354 #[codec(index = 2)]
355 V2(v2::Junction),
356 #[codec(index = 3)]
357 V3(v3::Junction),
358 #[codec(index = 4)]
359 V4(v4::Junction),
360 }
361}
362
363versioned_type! {
364 #[derive(Ord, PartialOrd)]
366 pub enum VersionedLocation {
367 #[codec(index = 1)] V2(v2::MultiLocation),
369 #[codec(index = 3)]
370 V3(v3::MultiLocation),
371 #[codec(index = 4)]
372 V4(v4::Location),
373 }
374}
375
376#[deprecated(note = "Use `VersionedLocation` instead")]
377pub type VersionedMultiLocation = VersionedLocation;
378
379versioned_type! {
380 pub enum VersionedInteriorLocation {
382 #[codec(index = 2)] V2(v2::InteriorMultiLocation),
384 #[codec(index = 3)]
385 V3(v3::InteriorMultiLocation),
386 #[codec(index = 4)]
387 V4(v4::InteriorLocation),
388 }
389}
390
391#[deprecated(note = "Use `VersionedInteriorLocation` instead")]
392pub type VersionedInteriorMultiLocation = VersionedInteriorLocation;
393
394versioned_type! {
395 pub enum VersionedAsset {
397 #[codec(index = 1)] V2(v2::MultiAsset),
399 #[codec(index = 3)]
400 V3(v3::MultiAsset),
401 #[codec(index = 4)]
402 V4(v4::Asset),
403 }
404}
405
406#[deprecated(note = "Use `VersionedAsset` instead")]
407pub type VersionedMultiAsset = VersionedAsset;
408
409versioned_type! {
410 pub enum VersionedAssets {
412 #[codec(index = 1)] V2(v2::MultiAssets),
414 #[codec(index = 3)]
415 V3(v3::MultiAssets),
416 #[codec(index = 4)]
417 V4(v4::Assets),
418 }
419}
420
421#[deprecated(note = "Use `VersionedAssets` instead")]
422pub type VersionedMultiAssets = VersionedAssets;
423
424#[derive(Derivative, Encode, Decode, TypeInfo)]
426#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
427#[codec(encode_bound())]
428#[codec(decode_bound())]
429#[scale_info(bounds(), skip_type_params(RuntimeCall))]
430#[scale_info(replace_segment("staging_xcm", "xcm"))]
431pub enum VersionedXcm<RuntimeCall> {
432 #[codec(index = 2)]
433 #[deprecated]
434 V2(v2::Xcm<RuntimeCall>),
435 #[codec(index = 3)]
436 V3(v3::Xcm<RuntimeCall>),
437 #[codec(index = 4)]
438 V4(v4::Xcm<RuntimeCall>),
439}
440
441impl<C> IntoVersion for VersionedXcm<C> {
442 fn into_version(self, n: Version) -> Result<Self, ()> {
443 Ok(match n {
444 2 => Self::V2(self.try_into()?),
445 3 => Self::V3(self.try_into()?),
446 4 => Self::V4(self.try_into()?),
447 _ => return Err(()),
448 })
449 }
450}
451
452impl<C> IdentifyVersion for VersionedXcm<C> {
453 fn identify_version(&self) -> Version {
454 match self {
455 Self::V2(_) => v2::VERSION,
456 Self::V3(_) => v3::VERSION,
457 Self::V4(_) => v4::VERSION,
458 }
459 }
460}
461
462impl<C> VersionedXcm<C> {
463 pub fn validate_xcm_nesting(&self) -> Result<(), ()> {
469 self.using_encoded(|mut enc| {
470 Self::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut enc).map(|_| ())
471 })
472 .map_err(|e| {
473 log::error!(target: "xcm::validate_xcm_nesting", "Decode error: {e:?} for xcm: {self:?}!");
474 ()
475 })
476 }
477}
478
479impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
480 fn from(x: v2::Xcm<RuntimeCall>) -> Self {
481 VersionedXcm::V2(x)
482 }
483}
484
485impl<RuntimeCall> From<v3::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
486 fn from(x: v3::Xcm<RuntimeCall>) -> Self {
487 VersionedXcm::V3(x)
488 }
489}
490
491impl<RuntimeCall> From<v4::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
492 fn from(x: v4::Xcm<RuntimeCall>) -> Self {
493 VersionedXcm::V4(x)
494 }
495}
496
497impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v2::Xcm<RuntimeCall> {
498 type Error = ();
499 fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
500 use VersionedXcm::*;
501 match x {
502 V2(x) => Ok(x),
503 V3(x) => x.try_into(),
504 V4(x) => {
505 let v3: v3::Xcm<RuntimeCall> = x.try_into()?;
506 v3.try_into()
507 },
508 }
509 }
510}
511
512impl<Call> TryFrom<VersionedXcm<Call>> for v3::Xcm<Call> {
513 type Error = ();
514 fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
515 use VersionedXcm::*;
516 match x {
517 V2(x) => x.try_into(),
518 V3(x) => Ok(x),
519 V4(x) => x.try_into(),
520 }
521 }
522}
523
524impl<Call> TryFrom<VersionedXcm<Call>> for v4::Xcm<Call> {
525 type Error = ();
526 fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
527 use VersionedXcm::*;
528 match x {
529 V2(x) => {
530 let v3: v3::Xcm<Call> = x.try_into()?;
531 v3.try_into()
532 },
533 V3(x) => x.try_into(),
534 V4(x) => Ok(x),
535 }
536 }
537}
538
539pub trait WrapVersion {
542 fn wrap_version<RuntimeCall>(
543 dest: &latest::Location,
544 xcm: impl Into<VersionedXcm<RuntimeCall>>,
545 ) -> Result<VersionedXcm<RuntimeCall>, ()>;
546}
547
548pub trait IdentifyVersion {
551 fn identify_version(&self) -> Version;
552}
553
554pub trait GetVersion {
557 fn get_version_for(dest: &latest::Location) -> Option<Version>;
558}
559
560impl WrapVersion for () {
563 fn wrap_version<RuntimeCall>(
564 _: &latest::Location,
565 xcm: impl Into<VersionedXcm<RuntimeCall>>,
566 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
567 Ok(xcm.into())
568 }
569}
570
571pub struct AlwaysV2;
574impl WrapVersion for AlwaysV2 {
575 fn wrap_version<RuntimeCall>(
576 _: &latest::Location,
577 xcm: impl Into<VersionedXcm<RuntimeCall>>,
578 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
579 Ok(VersionedXcm::<RuntimeCall>::V2(xcm.into().try_into()?))
580 }
581}
582impl GetVersion for AlwaysV2 {
583 fn get_version_for(_dest: &latest::Location) -> Option<Version> {
584 Some(v2::VERSION)
585 }
586}
587
588pub struct AlwaysV3;
591impl WrapVersion for AlwaysV3 {
592 fn wrap_version<Call>(
593 _: &latest::Location,
594 xcm: impl Into<VersionedXcm<Call>>,
595 ) -> Result<VersionedXcm<Call>, ()> {
596 Ok(VersionedXcm::<Call>::V3(xcm.into().try_into()?))
597 }
598}
599impl GetVersion for AlwaysV3 {
600 fn get_version_for(_dest: &latest::Location) -> Option<Version> {
601 Some(v3::VERSION)
602 }
603}
604
605pub struct AlwaysV4;
608impl WrapVersion for AlwaysV4 {
609 fn wrap_version<Call>(
610 _: &latest::Location,
611 xcm: impl Into<VersionedXcm<Call>>,
612 ) -> Result<VersionedXcm<Call>, ()> {
613 Ok(VersionedXcm::<Call>::V4(xcm.into().try_into()?))
614 }
615}
616impl GetVersion for AlwaysV4 {
617 fn get_version_for(_dest: &latest::Location) -> Option<Version> {
618 Some(v4::VERSION)
619 }
620}
621
622pub type AlwaysLatest = AlwaysV4;
625
626pub type AlwaysLts = AlwaysV4;
629
630pub mod prelude {
631 pub use super::{
632 latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, AlwaysV4, GetVersion,
633 IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset,
634 VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation,
635 VersionedResponse, VersionedXcm, WrapVersion,
636 };
637}
638
639pub mod opaque {
640 pub mod v2 {
641 pub use crate::v2::*;
643 pub use crate::v2::opaque::{Instruction, Xcm};
645 }
646 pub mod v3 {
647 pub use crate::v3::*;
649 pub use crate::v3::opaque::{Instruction, Xcm};
651 }
652 pub mod v4 {
653 pub use crate::v4::*;
655 pub use crate::v4::opaque::{Instruction, Xcm};
657 }
658
659 pub mod latest {
660 pub use super::v4::*;
661 }
662
663 pub mod lts {
664 pub use super::v4::*;
665 }
666
667 pub type VersionedXcm = super::VersionedXcm<()>;
669}
670
671#[test]
672fn conversion_works() {
673 use latest::prelude::*;
674 let assets: Assets = (Here, 1u128).into();
675 let _: VersionedAssets = assets.into();
676}
677
678#[test]
679fn size_limits() {
680 extern crate std;
681
682 let mut test_failed = false;
683 macro_rules! check_sizes {
684 ($(($kind:ty, $expected:expr),)+) => {
685 $({
686 let s = core::mem::size_of::<$kind>();
687 if s > $expected {
692 test_failed = true;
693 std::eprintln!(
694 "assertion failed: size of '{}' is {} (which is more than the expected {})",
695 stringify!($kind),
696 s,
697 $expected
698 );
699 } else {
700 std::println!(
701 "type '{}' is of size {} which is within the expected {}",
702 stringify!($kind),
703 s,
704 $expected
705 );
706 }
707 })+
708 }
709 }
710
711 check_sizes! {
712 (crate::latest::Instruction<()>, 112),
713 (crate::latest::Asset, 80),
714 (crate::latest::Location, 24),
715 (crate::latest::AssetId, 40),
716 (crate::latest::Junctions, 16),
717 (crate::latest::Junction, 88),
718 (crate::latest::Response, 40),
719 (crate::latest::AssetInstance, 48),
720 (crate::latest::NetworkId, 48),
721 (crate::latest::BodyId, 32),
722 (crate::latest::Assets, 24),
723 (crate::latest::BodyPart, 12),
724 }
725 assert!(!test_failed);
726}
727
728#[test]
729fn validate_xcm_nesting_works() {
730 use crate::latest::{
731 prelude::{GeneralIndex, ReserveAssetDeposited, SetAppendix},
732 Assets, Xcm, MAX_INSTRUCTIONS_TO_DECODE, MAX_ITEMS_IN_ASSETS,
733 };
734
735 let assets = |count| {
737 let mut assets = Assets::new();
738 for i in 0..count {
739 assets.push((GeneralIndex(i as u128), 100).into());
740 }
741 assets
742 };
743
744 let with_instr = |depth| {
746 let mut xcm = Xcm::<()>(vec![]);
747 for _ in 0..depth - 1 {
748 xcm = Xcm::<()>(vec![SetAppendix(xcm)]);
749 }
750 xcm
751 };
752
753 assert!(VersionedXcm::<()>::from(Xcm(vec![
755 ReserveAssetDeposited(assets(1));
756 (MAX_INSTRUCTIONS_TO_DECODE - 1) as usize
757 ]))
758 .validate_xcm_nesting()
759 .is_ok());
760 assert!(VersionedXcm::<()>::from(Xcm(vec![
761 ReserveAssetDeposited(assets(1));
762 MAX_INSTRUCTIONS_TO_DECODE as usize
763 ]))
764 .validate_xcm_nesting()
765 .is_ok());
766 assert!(VersionedXcm::<()>::from(Xcm(vec![
767 ReserveAssetDeposited(assets(1));
768 (MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
769 ]))
770 .validate_xcm_nesting()
771 .is_err());
772
773 assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH - 1))
775 .validate_xcm_nesting()
776 .is_ok());
777 assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH))
778 .validate_xcm_nesting()
779 .is_ok());
780 assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH + 1))
781 .validate_xcm_nesting()
782 .is_err());
783
784 assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
786 MAX_ITEMS_IN_ASSETS
787 ))]))
788 .validate_xcm_nesting()
789 .is_ok());
790 assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
791 MAX_ITEMS_IN_ASSETS - 1
792 ))]))
793 .validate_xcm_nesting()
794 .is_ok());
795 assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
796 MAX_ITEMS_IN_ASSETS + 1
797 ))]))
798 .validate_xcm_nesting()
799 .is_err());
800}