diff --git a/Cargo.lock b/Cargo.lock index eef205be6328..442930fcb97b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13641,6 +13641,8 @@ name = "xcm" version = "0.9.38" dependencies = [ "derivative", + "hex", + "hex-literal", "impl-trait-for-tuples", "log", "parity-scale-codec", diff --git a/xcm/Cargo.toml b/xcm/Cargo.toml index 3e0fcdb1f093..b56c8109160f 100644 --- a/xcm/Cargo.toml +++ b/xcm/Cargo.toml @@ -18,6 +18,8 @@ xcm-procedural = { path = "procedural" } [dev-dependencies] sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" } +hex = "0.4.3" +hex-literal = "0.3.4" [features] default = ["std"] diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 678a825b6608..70fe1158ca44 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -41,6 +41,9 @@ pub mod latest { 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; @@ -73,6 +76,7 @@ pub trait TryAs { macro_rules! versioned_type { ($(#[$attr:meta])* pub enum $n:ident { + $(#[$index3:meta])+ V3($v3:ty), }) => { #[derive(Derivative, Encode, Decode, TypeInfo)] @@ -86,7 +90,7 @@ macro_rules! versioned_type { #[codec(decode_bound())] $(#[$attr])* pub enum $n { - #[codec(index = 0)] + $(#[$index3])* V3($v3), } impl $n { @@ -131,7 +135,9 @@ macro_rules! versioned_type { }; ($(#[$attr:meta])* pub enum $n:ident { + $(#[$index2:meta])+ V2($v2:ty), + $(#[$index3:meta])+ V3($v3:ty), }) => { #[derive(Derivative, Encode, Decode, TypeInfo)] @@ -145,9 +151,9 @@ macro_rules! versioned_type { #[codec(decode_bound())] $(#[$attr])* pub enum $n { - #[codec(index = 0)] + $(#[$index2])* V2($v2), - #[codec(index = 1)] + $(#[$index3])* V3($v3), } impl $n { @@ -216,93 +222,12 @@ macro_rules! versioned_type { } } }; - - ($(#[$attr:meta])* pub enum $n:ident { - V2($v2:ty), - 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 { - #[codec(index = 1)] - V2($v2), - #[codec(index = 2)] - 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 { - 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), } } @@ -310,7 +235,9 @@ versioned_type! { 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), } } @@ -319,7 +246,9 @@ 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), } } @@ -327,7 +256,9 @@ versioned_type! { 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), } } @@ -335,7 +266,9 @@ versioned_type! { 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), } } @@ -343,7 +276,9 @@ versioned_type! { 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), } } diff --git a/xcm/src/tests.rs b/xcm/src/tests.rs new file mode 100644 index 000000000000..32326cab0333 --- /dev/null +++ b/xcm/src/tests.rs @@ -0,0 +1,183 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 . + +use crate::*; +use alloc::vec; + +#[test] +fn encode_decode_versioned_asset_id_v3() { + let asset_id = VersionedAssetId::V3(v3::AssetId::Abstract([1; 32])); + let encoded = asset_id.encode(); + + assert_eq!( + encoded, + hex_literal::hex!("03010101010101010101010101010101010101010101010101010101010101010101"), + "encode format changed" + ); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedAssetId::decode(&mut &encoded[..]).unwrap(); + assert_eq!(asset_id, decoded); +} + +#[test] +fn encode_decode_versioned_response_v2() { + let response = VersionedResponse::V2(v2::Response::Null); + let encoded = response.encode(); + + assert_eq!(encoded, hex_literal::hex!("0200"), "encode format changed"); + assert_eq!(encoded[0], 2, "bad version number"); + + let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap(); + assert_eq!(response, decoded); +} + +#[test] +fn encode_decode_versioned_response_v3() { + let response = VersionedResponse::V3(v3::Response::Null); + let encoded = response.encode(); + + assert_eq!(encoded, hex_literal::hex!("0300"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap(); + assert_eq!(response, decoded); +} + +#[test] +fn encode_decode_versioned_multi_location_v2() { + let location = VersionedMultiLocation::V2(v2::MultiLocation::new(0, v2::Junctions::Here)); + let encoded = location.encode(); + + assert_eq!(encoded, hex_literal::hex!("010000"), "encode format changed"); + assert_eq!(encoded[0], 1, "bad version number"); // this is introduced in v1 + + let decoded = VersionedMultiLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); +} + +#[test] +fn encode_decode_versioned_multi_location_v3() { + let location = VersionedMultiLocation::V3(v3::MultiLocation::new(0, v3::Junctions::Here)); + let encoded = location.encode(); + + assert_eq!(encoded, hex_literal::hex!("030000"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedMultiLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); +} + +#[test] +fn encode_decode_versioned_interior_multi_location_v2() { + let location = VersionedInteriorMultiLocation::V2(v2::InteriorMultiLocation::Here); + let encoded = location.encode(); + + assert_eq!(encoded, hex_literal::hex!("0200"), "encode format changed"); + assert_eq!(encoded[0], 2, "bad version number"); + + let decoded = VersionedInteriorMultiLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); +} + +#[test] +fn encode_decode_versioned_interior_multi_location_v3() { + let location = VersionedInteriorMultiLocation::V3(v3::InteriorMultiLocation::Here); + let encoded = location.encode(); + + assert_eq!(encoded, hex_literal::hex!("0300"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedInteriorMultiLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); +} + +#[test] +fn encode_decode_versioned_multi_asset_v2() { + let asset = VersionedMultiAsset::V2(v2::MultiAsset::from(((0, v2::Junctions::Here), 1))); + let encoded = asset.encode(); + + assert_eq!(encoded, hex_literal::hex!("010000000004"), "encode format changed"); + assert_eq!(encoded[0], 1, "bad version number"); + + let decoded = VersionedMultiAsset::decode(&mut &encoded[..]).unwrap(); + assert_eq!(asset, decoded); +} + +#[test] +fn encode_decode_versioned_multi_asset_v3() { + let asset = VersionedMultiAsset::V3(v3::MultiAsset::from((v3::MultiLocation::default(), 1))); + let encoded = asset.encode(); + + assert_eq!(encoded, hex_literal::hex!("030000000004"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedMultiAsset::decode(&mut &encoded[..]).unwrap(); + assert_eq!(asset, decoded); +} + +#[test] +fn encode_decode_versioned_multi_assets_v2() { + let assets = VersionedMultiAssets::V2(v2::MultiAssets::from(vec![v2::MultiAsset::from(( + (0, v2::Junctions::Here), + 1, + ))])); + let encoded = assets.encode(); + + assert_eq!(encoded, hex_literal::hex!("01040000000004"), "encode format changed"); + assert_eq!(encoded[0], 1, "bad version number"); + + let decoded = VersionedMultiAssets::decode(&mut &encoded[..]).unwrap(); + assert_eq!(assets, decoded); +} + +#[test] +fn encode_decode_versioned_multi_assets_v3() { + let assets = VersionedMultiAssets::V3(v3::MultiAssets::from(vec![ + (v3::MultiAsset::from((v3::MultiLocation::default(), 1))), + ])); + let encoded = assets.encode(); + + assert_eq!(encoded, hex_literal::hex!("03040000000004"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedMultiAssets::decode(&mut &encoded[..]).unwrap(); + assert_eq!(assets, decoded); +} + +#[test] +fn encode_decode_versioned_xcm_v2() { + let xcm = VersionedXcm::V2(v2::Xcm::<()>::new()); + let encoded = xcm.encode(); + + assert_eq!(encoded, hex_literal::hex!("0200"), "encode format changed"); + assert_eq!(encoded[0], 2, "bad version number"); + + let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap(); + assert_eq!(xcm, decoded); +} + +#[test] +fn encode_decode_versioned_xcm_v3() { + let xcm = VersionedXcm::V3(v3::Xcm::<()>::new()); + let encoded = xcm.encode(); + + assert_eq!(encoded, hex_literal::hex!("0300"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); + + let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap(); + assert_eq!(xcm, decoded); +}