// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . //! Cross-Consensus Message format data structures. // NOTE, this crate is meant to be used in many different environments, notably wasm, but not // necessarily related to FRAME or even Substrate. // // Hence, `no_std` rather than sp-runtime. #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; use derivative::Derivative; use parity_scale_codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen}; use scale_info::TypeInfo; pub mod v2; pub mod v3; pub mod lts { pub use super::v3::*; } pub mod latest { pub use super::v3::*; } mod double_encoded; pub use double_encoded::DoubleEncoded; #[cfg(test)] mod tests; /// Maximum nesting level for XCM decoding. pub const MAX_XCM_DECODE_DEPTH: u32 = 8; /// A version of XCM. pub type Version = u32; #[derive(Clone, Eq, PartialEq, Debug)] pub enum Unsupported {} impl Encode for Unsupported {} impl Decode for Unsupported { fn decode(_: &mut I) -> Result { Err("Not decodable".into()) } } /// Attempt to convert `self` into a particular version of itself. pub trait IntoVersion: Sized { /// Consume `self` and return same value expressed in some particular `version` of XCM. fn into_version(self, version: Version) -> Result; /// Consume `self` and return same value expressed the latest version of XCM. fn into_latest(self) -> Result { self.into_version(latest::VERSION) } } pub trait TryAs { fn try_as(&self) -> Result<&T, ()>; } macro_rules! versioned_type { ($(#[$attr:meta])* pub enum $n:ident { $(#[$index3:meta])+ V3($v3:ty), }) => { #[derive(Derivative, Encode, Decode, TypeInfo)] #[derivative( Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = "") )] #[codec(encode_bound())] #[codec(decode_bound())] $(#[$attr])* pub enum $n { $(#[$index3])* V3($v3), } impl $n { pub fn try_as(&self) -> Result<&T, ()> where Self: TryAs { >::try_as(&self) } } impl TryAs<$v3> for $n { fn try_as(&self) -> Result<&$v3, ()> { match &self { Self::V3(ref x) => Ok(x), } } } impl IntoVersion for $n { fn into_version(self, n: Version) -> Result { Ok(match n { 3 => Self::V3(self.try_into()?), _ => return Err(()), }) } } impl> From for $n { fn from(x: T) -> Self { $n::V3(x.into()) } } impl TryFrom<$n> for $v3 { type Error = (); fn try_from(x: $n) -> Result { use $n::*; match x { V3(x) => Ok(x), } } } impl MaxEncodedLen for $n { fn max_encoded_len() -> usize { <$v3>::max_encoded_len() } } }; ($(#[$attr:meta])* pub enum $n:ident { $(#[$index2:meta])+ V2($v2:ty), $(#[$index3:meta])+ V3($v3:ty), }) => { #[derive(Derivative, Encode, Decode, TypeInfo)] #[derivative( Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = "") )] #[codec(encode_bound())] #[codec(decode_bound())] $(#[$attr])* pub enum $n { $(#[$index2])* V2($v2), $(#[$index3])* V3($v3), } impl $n { pub fn try_as(&self) -> Result<&T, ()> where Self: TryAs { >::try_as(&self) } } impl TryAs<$v2> for $n { fn try_as(&self) -> Result<&$v2, ()> { match &self { Self::V2(ref x) => Ok(x), _ => Err(()), } } } impl TryAs<$v3> for $n { fn try_as(&self) -> Result<&$v3, ()> { match &self { Self::V3(ref x) => Ok(x), _ => Err(()), } } } impl IntoVersion for $n { fn into_version(self, n: Version) -> Result { Ok(match n { 1 | 2 => Self::V2(self.try_into()?), 3 => Self::V3(self.try_into()?), _ => return Err(()), }) } } impl From<$v2> for $n { fn from(x: $v2) -> Self { $n::V2(x) } } impl> From for $n { fn from(x: T) -> Self { $n::V3(x.into()) } } impl TryFrom<$n> for $v2 { type Error = (); fn try_from(x: $n) -> Result { use $n::*; match x { V2(x) => Ok(x), V3(x) => x.try_into(), } } } impl TryFrom<$n> for $v3 { type Error = (); fn try_from(x: $n) -> Result { use $n::*; match x { V2(x) => x.try_into(), V3(x) => Ok(x), } } } impl MaxEncodedLen for $n { fn max_encoded_len() -> usize { <$v3>::max_encoded_len() } } }; } versioned_type! { /// A single version's `Response` value, together with its version code. pub enum VersionedAssetId { #[codec(index = 3)] V3(v3::AssetId), } } versioned_type! { /// A single version's `Response` value, together with its version code. pub enum VersionedResponse { #[codec(index = 2)] V2(v2::Response), #[codec(index = 3)] V3(v3::Response), } } versioned_type! { /// A single `NetworkId` value, together with its version code. pub enum VersionedNetworkId { #[codec(index = 2)] V2(v2::NetworkId), #[codec(index = 3)] V3(v3::NetworkId), } } versioned_type! { /// A single `Junction` value, together with its version code. pub enum VersionedJunction { #[codec(index = 2)] V2(v2::Junction), #[codec(index = 3)] V3(v3::Junction), } } versioned_type! { /// A single `MultiLocation` value, together with its version code. #[derive(Ord, PartialOrd)] pub enum VersionedMultiLocation { #[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index V2(v2::MultiLocation), #[codec(index = 3)] V3(v3::MultiLocation), } } versioned_type! { /// A single `InteriorMultiLocation` value, together with its version code. pub enum VersionedInteriorMultiLocation { #[codec(index = 2)] // while this is same as v1::Junctions, VersionedInteriorMultiLocation is introduced in v3 V2(v2::InteriorMultiLocation), #[codec(index = 3)] V3(v3::InteriorMultiLocation), } } versioned_type! { /// A single `MultiAsset` value, together with its version code. pub enum VersionedMultiAsset { #[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index V2(v2::MultiAsset), #[codec(index = 3)] V3(v3::MultiAsset), } } versioned_type! { /// A single `MultiAssets` value, together with its version code. pub enum VersionedMultiAssets { #[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index V2(v2::MultiAssets), #[codec(index = 3)] V3(v3::MultiAssets), } } /// A single XCM message, together with its version code. #[derive(Derivative, Encode, Decode, TypeInfo)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] #[scale_info(bounds(), skip_type_params(RuntimeCall))] pub enum VersionedXcm { #[codec(index = 2)] V2(v2::Xcm), #[codec(index = 3)] V3(v3::Xcm), } impl IntoVersion for VersionedXcm { fn into_version(self, n: Version) -> Result { Ok(match n { 2 => Self::V2(self.try_into()?), 3 => Self::V3(self.try_into()?), _ => return Err(()), }) } } impl From> for VersionedXcm { fn from(x: v2::Xcm) -> Self { VersionedXcm::V2(x) } } impl From> for VersionedXcm { fn from(x: v3::Xcm) -> Self { VersionedXcm::V3(x) } } impl TryFrom> for v2::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { V2(x) => Ok(x), V3(x) => x.try_into(), } } } impl TryFrom> for v3::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { V2(x) => x.try_into(), V3(x) => Ok(x), } } } /// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `MultiLocation` which will /// interpret it. pub trait WrapVersion { fn wrap_version( dest: &latest::MultiLocation, xcm: impl Into>, ) -> Result, ()>; } /// `()` implementation does nothing with the XCM, just sending with whatever version it was /// authored as. impl WrapVersion for () { fn wrap_version( _: &latest::MultiLocation, xcm: impl Into>, ) -> Result, ()> { Ok(xcm.into()) } } /// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before /// wrapping it. pub struct AlwaysV2; impl WrapVersion for AlwaysV2 { fn wrap_version( _: &latest::MultiLocation, xcm: impl Into>, ) -> Result, ()> { Ok(VersionedXcm::::V2(xcm.into().try_into()?)) } } /// `WrapVersion` implementation which attempts to always convert the XCM to version 3 before /// wrapping it. pub struct AlwaysV3; impl WrapVersion for AlwaysV3 { fn wrap_version( _: &latest::MultiLocation, xcm: impl Into>, ) -> Result, ()> { Ok(VersionedXcm::::V3(xcm.into().try_into()?)) } } /// `WrapVersion` implementation which attempts to always convert the XCM to the latest version /// before wrapping it. pub type AlwaysLatest = AlwaysV3; /// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long- /// Term-Support version before wrapping it. pub type AlwaysLts = AlwaysV3; pub mod prelude { pub use super::{ latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, IntoVersion, Unsupported, Version as XcmVersion, VersionedAssetId, VersionedInteriorMultiLocation, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WrapVersion, }; } pub mod opaque { pub mod v2 { // Everything from v2 pub use crate::v2::*; // Then override with the opaque types in v2 pub use crate::v2::opaque::{Instruction, Xcm}; } pub mod v3 { // Everything from v3 pub use crate::v3::*; // Then override with the opaque types in v3 pub use crate::v3::opaque::{Instruction, Xcm}; } pub mod latest { pub use super::v3::*; } pub mod lts { pub use super::v3::*; } /// The basic `VersionedXcm` type which just uses the `Vec` as an encoded call. pub type VersionedXcm = super::VersionedXcm<()>; } #[test] fn conversion_works() { use latest::prelude::*; let _: VersionedMultiAssets = (Here, 1u128).into(); }